Docker Compose setup for self-hosting Planka + the MCP server.
Already running Planka? You only need the
mcp-serverservice — drop everything else from the compose and pointPLANKA_URLat your existing instance.Planka docs: docs.planka.cloud — configuration, upgrades, backup, etc.
- Docker + Docker Compose v2
Create these two files in the same directory and run docker compose up -d.
services:
planka:
image: ghcr.io/plankanban/planka:2.0.1
restart: unless-stopped
ports:
- "1337:1337"
volumes:
- ./data/planka-data:/app/data
environment:
- BASE_URL=${PLANKA_BASE_URL}
- DATABASE_URL=postgresql://${POSTGRES_USER}@postgres:5432/${POSTGRES_PLANKA_DB}
- SECRET_KEY=${PLANKA_SECRET_KEY}
- DEFAULT_ADMIN_EMAIL=${PLANKA_ADMIN_EMAIL}
- DEFAULT_ADMIN_PASSWORD=${PLANKA_ADMIN_PASSWORD}
- DEFAULT_ADMIN_NAME=${PLANKA_ADMIN_NAME}
- DEFAULT_ADMIN_USERNAME=${PLANKA_ADMIN_USERNAME}
- S3_ENDPOINT=http://minio:9000
- S3_REGION=${MINIO_REGION}
- S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
- S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
- S3_BUCKET=${MINIO_BUCKET}
- S3_FORCE_PATH_STYLE=true
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_started
mcp-server:
image: ghcr.io/ashkuc/charlieplan-mcp/mcp-server:${MCP_IMAGE_TAG}
restart: unless-stopped
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://${POSTGRES_USER}@postgres:5432/${POSTGRES_MCP_DB}
- PLANKA_URL=http://planka:1337
- SERVER_URL=${MCP_SERVER_URL}
- CODE_SECRET=${MCP_CODE_SECRET}
depends_on:
postgres:
condition: service_healthy
planka:
condition: service_started
postgres:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- ./data/postgres-data:/var/lib/postgresql/data
- ./postgres-init:/docker-entrypoint-initdb.d
environment:
- POSTGRES_DB=${POSTGRES_PLANKA_DB}
- POSTGRES_HOST_AUTH_METHOD=trust
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d ${POSTGRES_PLANKA_DB}"]
interval: 30s
timeout: 5s
retries: 5
start_period: 10s
minio:
image: minio/minio:latest
restart: unless-stopped
command: server /data --console-address ":9001"
environment:
- MINIO_ROOT_USER=${MINIO_ROOT_USER}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
volumes:
- ./data/minio-data:/data
healthcheck:
test: ["CMD", "mc", "ready", "local"]
interval: 30s
timeout: 5s
retries: 5
start_period: 10s-- Creates the charlieplan database for the MCP server.
-- The planka database is created automatically via POSTGRES_DB env var.
CREATE DATABASE "charlieplan";
GRANT ALL PRIVILEGES ON DATABASE "charlieplan" TO postgres;# ── MCP Server ────────────────────────────────────────────────────────────────
# Image tag to deploy. Find available tags in the GitHub Container Registry.
MCP_IMAGE_TAG=latest
# ── Planka ────────────────────────────────────────────────────────────────────
# Public base URL of the Planka instance (no trailing slash)
PLANKA_BASE_URL=https://planka.example.com
# Encryption key — generate: openssl rand -hex 64
PLANKA_SECRET_KEY=
# Default admin account, created on first run
PLANKA_ADMIN_EMAIL=admin@example.com
PLANKA_ADMIN_PASSWORD=
PLANKA_ADMIN_NAME=Admin
PLANKA_ADMIN_USERNAME=admin
# ── MinIO ─────────────────────────────────────────────────────────────────────
MINIO_ROOT_USER=minioadmin
# Generate: openssl rand -hex 16
MINIO_ROOT_PASSWORD=
MINIO_BUCKET=planka
MINIO_REGION=us-east-1
# ── MCP Server ────────────────────────────────────────────────────────────────
# Public URL of the MCP server (no trailing slash). Required for OAuth.
MCP_SERVER_URL=https://charlieplan-mcp.example.com
# OAuth authorization code signing key — generate: openssl rand -hex 32
MCP_CODE_SECRET=
# ── PostgreSQL ────────────────────────────────────────────────────────────────
POSTGRES_USER=postgres
POSTGRES_PLANKA_DB=planka
POSTGRES_MCP_DB=charlieplan1. Fill in .env
At minimum set PLANKA_BASE_URL and MCP_SERVER_URL to your public URLs, then generate secrets:
openssl rand -hex 64 # PLANKA_SECRET_KEY
openssl rand -hex 32 # MCP_CODE_SECRET
openssl rand -hex 16 # MINIO_ROOT_PASSWORD2. Start services
docker compose up -dPostgres runs the init script on first start — creates both planka and charlieplan databases.
The MCP server runs Drizzle migrations automatically on startup.
3. Verify
curl http://localhost:3000/health
# → {"ok":true}4. Create a Planka API key
Log in to Planka → Profile → API Keys → create a key. This is what MCP clients pass as a Bearer token.
Tip: create a dedicated non-admin user for MCP usage and generate the key from that account.
5. Connect an MCP client
Point your client at http://host:3000/mcp and authenticate via the OAuth flow with the API key.
# Update MCP_IMAGE_TAG in .env, then:
docker compose pull mcp-server
docker compose up -d mcp-serverAll persistent data is in ./data/. Back up this directory.
| Path | Contents |
|---|---|
./data/planka-data |
Planka uploads |
./data/postgres-data |
PostgreSQL data |
./data/minio-data |
MinIO object storage |