planka Documentation

Elegant open source project tracking. Self-hosted Kanban for teams — free Community edition, with PLANKA Pro for organisations.

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy planka on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name planka -p 3000:3000 --restart always ghcr.io/plankanban/planka:latest
Configured Services & Ports
⚙️ planka
image: ghcr.io/plankanban/planka:latest
🔌 Ports: 3000:1337
⚙️ postgres
image: postgres:16-alpine
docker-compose.yml
yaml (docker-compose.yml)
services:
  planka:
    image: ghcr.io/plankanban/planka:latest
    restart: on-failure
    volumes:
      - data:/app/data
      # - ./terms:/app/terms/custom
    # Optionally override this to your user/group
    # user: 1000:1000
    # tmpfs:
    #   - /app/.tmp:mode=770,uid=1000,gid=1000
    ports:
      - 3000:1337
    environment:
      - BASE_URL=http://localhost:3000
      - DATABASE_URL=postgresql://postgres@postgres/planka

      # Optionally store the database password in secrets:
      # - DATABASE_URL=postgresql://postgres:$${DATABASE_PASSWORD}@postgres/planka
      # - DATABASE_PASSWORD__FILE=/run/secrets/database_password
      # And add the following to the service:
      # secrets:
      #   - database_password

      # REPLACE THIS. Every access token is signed with it, so leaving the
      # published example value means anyone can mint a token for any account
      # on this instance. Generate one with: openssl rand -hex 32
      # PLANKA warns about this on every start until you change it.
      - SECRET_KEY=notsecretkey
      # Optionally store in secrets - then SECRET_KEY should not be set
      # - SECRET_KEY__FILE=/run/secrets/secret_key

      # - LOG_LEVEL=warn

      # - TRUST_PROXY=true
      # - MAX_UPLOAD_FILE_SIZE=
      # - TOKEN_EXPIRES_IN=365 # In days

      # - STORAGE_LIMIT=
      # - ACTIVE_USERS_LIMIT=

      # related: https://github.com/knex/knex/issues/2354
      # As knex does not pass query parameters from the connection string,
      # we have to use environment variables in order to pass the desired values, e.g.
      # - PGSSLMODE=

      # Configure knex to accept SSL certificates
      # - KNEX_REJECT_UNAUTHORIZED_SSL_CERTIFICATE=false

      # The default application language used as a fallback when a user's language is not set.
      # This language is also used for per-board notifications.
      # - DEFAULT_LANGUAGE=en-US

      # Do not comment out DEFAULT_ADMIN_EMAIL if you want to prevent this user from being edited/deleted
      # - [email protected]
      # - DEFAULT_ADMIN_PASSWORD=demo
      # Optionally store in secrets - then DEFAULT_ADMIN_PASSWORD should not be set
      # - DEFAULT_ADMIN_PASSWORD__FILE=/run/secrets/default_admin_password
      # - DEFAULT_ADMIN_NAME=Demo Demo
      # - DEFAULT_ADMIN_USERNAME=demo

      # Set to true to show more detailed authentication error messages.
      # It should not be enabled without a rate limiter for security reasons.
      # - SHOW_DETAILED_AUTH_ERRORS=false

      # All outgoing HTTP requests (SMTP, webhooks, Apprise notifications, favicon fetching, etc.)
      # will be sent through this proxy if set.
      # If commented out, an internal Squid proxy will be started inside the container,
      # which you can control via OUTGOING_BLOCKED_* and OUTGOING_ALLOWED_* below.
      # - OUTGOING_PROXY=http://proxy:3128

      # Set to true to expose the Swagger specification at /swagger.json
      # - SWAGGER_EXPOSED=false

      # - S3_ENDPOINT=
      # - S3_REGION=
      # - S3_ACCESS_KEY_ID=
      # - S3_SECRET_ACCESS_KEY=
      # Optionally store in secrets - then S3_SECRET_ACCESS_KEY should not be set
      # - S3_SECRET_ACCESS_KEY__FILE=/run/secrets/s3_secret_access_key
      # - S3_BUCKET=
      # - S3_FORCE_PATH_STYLE=true
      # - S3_REQUEST_CHECKSUM_CALCULATION=

      # Email Notifications (https://nodemailer.com/smtp/)
      # These values override and disable configuration in the UI if set.
      # - SMTP_HOST=
      # - SMTP_PORT=587
      # - SMTP_NAME=
      # - SMTP_SECURE=true
      # - SMTP_TLS_REJECT_UNAUTHORIZED=false
      # - SMTP_USER=
      # - SMTP_PASSWORD=
      # Optionally store in secrets - then SMTP_PASSWORD should not be set
      # - SMTP_PASSWORD__FILE=/run/secrets/smtp_password
      # - SMTP_FROM="Demo Demo" 

      # Using Gravatar directly exposes user IPs and hashed emails to a third party (GDPR risk).
      # Use a proxy you control for privacy, or leave commented out or empty to disable.
      # - GRAVATAR_BASE_URL=https://www.gravatar.com/avatar/

      # --------------------------------------------------------------------
      # Outgoing traffic control (internal Squid proxy)
      # --------------------------------------------------------------------

      # These IPs/hostnames will always be blocked (highest priority)
      # - OUTGOING_BLOCKED_IPS=
      # - OUTGOING_BLOCKED_HOSTS=localhost,postgres

      # Only these IPs/hostnames will be reachable
      # - OUTGOING_ALLOWED_IPS=
      # - OUTGOING_ALLOWED_HOSTS=
    depends_on:
      postgres:
        condition: service_healthy

  postgres:
    image: postgres:16-alpine
    restart: on-failure
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=planka
      - POSTGRES_HOST_AUTH_METHOD=trust
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d planka"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  data:
  db-data:

Technical Documentation: plankanban/planka

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)


Community Pro
Boards, lists, cards, real-time collaboration ✓ ✓
Markdown, attachments, comments, custom fields ✓ ✓
Stopwatch and due dates on cards ✓ ✓
Notifications through 100+ providers ✓ ✓
Webhooks and a documented REST API ✓ ✓
Two-factor authentication ✓ ✓
Self-hosted, your database, your backups ✓ ✓
Free to self-host ✓ —
Phones and tablets, five themes, accent color — ✓
Calendar, timeline, map and media views — ✓
Recurring cards, card and board templates — ✓
One card on more than one board — ✓
Guest roles, SSO (OIDC) — ✓
PDF and HTML export — ✓
Support from the developers, no ticket queue — ✓

Pro upgrades an existing Community installation in place — your database, your volume, your
data. It is a real migration rather than a new image: Pro adds a Valkey service and a one-off
schema promote. The promote accepts only certain Community releases as a starting point —
currently 2.1.x and 2.2.x — and from any other version it stops without changing anything.
The upgrade is one-way — there is no downgrade back to Community — so take a backup first,
or try it on a copy.
The step-by-step guide is in the customer center once you have a key,
and lists the releases the promote accepts.


Key Features

  • Collaborative Kanban Boards: Create projects, boards, lists, cards, and manage tasks with an intuitive drag-and-drop interface
  • Real-Time Updates: Instant syncing across all users, no refresh needed
  • Rich Markdown Support: Write beautifully formatted card descriptions with a powerful markdown editor
  • Flexible Notifications: Get alerts through 100+ providers, fully customizable to your workflow
  • Multilingual & Easy to Translate: Full internationalization support for a global audience

How to Deploy

PLANKA is easy to install using multiple methods - learn more in the installation guide.

For configuration and environment settings, see the configuration section.

For hosted plans and pricing, see our website.

Notes App

A testing version of the Notes app is now available on multiple platforms:

  • iOS: Join the TestFlight to try the app
  • Windows & Android: Download the app here

Contact

For any security issues, please do not create a public issue on GitHub - instead, report it privately by emailing [email protected].

Note: We do NOT offer any public support via email, please use GitHub.

Join our community: Get help, share ideas, or contribute on our Discord server.

License

PLANKA is fair-code distributed under the Fair Use License and PLANKA Pro/Enterprise License.

  • Source Available: The source code is always visible
  • Self-Hostable: Deploy and host it anywhere
  • Extensible: Customize with your own functionality
  • Enterprise Licenses: Available for additional features and support

For more details, check the License Guide.

Contributing

Found a bug or have a feature request? Check out our Contributing Guide to get started.

For setting up the project locally, see the development section.

Thanks to all our contributors!


Overview (`charts/planka/README.md`)

PLANKA Helm Chart

This Helm Chart simplifies the deployment of PLANKA on Kubernetes.

Shoutout to this issue for requesting a Helm Chart!

Issues

By using the Bitnami chart for PostgreSQL, there is an issue where once deployed, if trying to use a different password then it will be ignored as the Persistant Volume (PV) will already exist with the previous password. See warning from Bitnami below:

Warning! Setting a password will be ignored on new installation in the case when previous Posgresql release was deleted through the helm command. In that case, old PVC will have an old password, and setting it through helm won't take effect. Deleting persistent volumes (PVs) will solve the issue. Refer to issue 2061 for more details

If you want to fully uninstall this chart including the data, follow these steps from the Bitnami Chart's docs.

Usage

If you just want to spin up an instance using help, please see these docs. If you want to make changes to the chart locally, and deploy them, see the below section.

Local Building and Using the Chart

The basic usage of the chart can be found below:

bash
git clone https://github.com/plankanban/planka.git
cd planka/charts/planka
helm dependency build
export SECRETKEY=$(openssl rand -hex 64)
helm install planka . --set secretkey=$SECRETKEY  \
--set admin_email="[email protected]"  \
--set admin_password="demo"  \
--set admin_name="Demo Demo" \
--set admin_username="demo"

Note: The command openssl rand -hex 64 is needed to create a random hexadecimal key for planka. On Windows you can use Git Bash to run that command.

To access PLANKA you can port forward using the following command:

bash
kubectl port-forward $POD_NAME 3000:1337

Accessing Externally

To access PLANKA externally you can use the following configuration

bash
# HTTP only
helm install planka . --set secretkey=$SECRETKEY \
--set admin_email="[email protected]"  \
--set admin_password="demo"  \
--set admin_name="Demo Demo" \
--set admin_username="demo" \
--set ingress.enabled=true \
--set ingress.hosts[0].host=planka.example.dev \

# HTTPS
helm install planka . --set secretkey=$SECRETKEY \
--set admin_email="[email protected]"  \
--set admin_password="demo"  \
--set admin_name="Demo Demo" \
--set admin_username="demo" \
--set ingress.enabled=true \
--set ingress.hosts[0].host=planka.example.dev \
--set ingress.tls[0].secretName=planka-tls \
--set ingress.tls[0].hosts[0]=planka.example.dev \

or create a values.yaml file like:

yaml
secretkey: "<InsertSecretKey>"
# The admin section needs to be present for new instances of PLANKA, after the first start you can remove the lines starting with admin_. If you want the admin user to be unchangeable admin_email: has to stay
# After changing the config you have to run ```helm upgrade  planka . -f values.yaml```

# Admin user
admin_email: "[email protected]" # Do not remove if you want to prevent this user from being edited/deleted
admin_password: "demo"
admin_name: "Demo Demo"
admin_username: "demo"
# Admin user

# Ingress
ingress:
  enabled: true
  hosts:
    - host: planka.example.dev
      paths:
        - path: /
          pathType: ImplementationSpecific

  # Needed for HTTPS
  tls:
    - secretName: planka-tls # existing TLS secret in k8s
      hosts:
        - planka.example.dev
```

```bash
helm install planka . -f values.yaml
```

### Things to consider if production hosting

If you want to host PLANKA for more than just playing around with, you might want to do the following things:

- Create a `values.yaml` with your config, as this will make applying upgrades much easier in the future.
- Create your `secretkey` once and store it either in a secure vault, or in your `values.yaml` file so it will be the same for upgrading in the future.
- Specify a password for `postgresql.auth.password` as there have been issues with the postgresql chart generating new passwords locking you out of the data you've already stored. (see [this issue](https://github.com/bitnami/charts/issues/2061))

Any questions or concerns, [raise an issue](https://github.com/Chris-Greaves/planka-helm-chart/issues/new).

## Advanced Configuration

### Extra Volume Mounts

The Helm chart supports mounting arbitrary ConfigMaps, Secrets, and Volumes to the PLANKA deployment using the `extraMounts` configuration. This is especially useful for scenarios like:

- Adding custom configuration files
- Mounting TLS certificates from existing secrets
- Adding temporary or persistent storage volumes

**Note**: ConfigMaps and Secrets must be created separately before referencing them in `extraMounts`.

#### Basic Usage

Use the `extraMounts` section to mount any type of volume:

```yaml
extraMounts:
  # Mount CA certificate from existing ConfigMap
  - name: ca-certs
    mountPath: /etc/ssl/certs/custom-ca.crt
    subPath: ca.crt
    readOnly: true
    configMap:
      name: ca-certificates # Must exist

  # Mount TLS certificates from existing Secret
  - name: tls-certs
    mountPath: /etc/ssl/private
    readOnly: true
    secret:
      secretName: planka-tls-secret # Must exist
      items:
        - key: tls.crt
          path: server.crt
        - key: tls.key
          path: server.key

  # Temporary storage
  - name: temp-storage
    mountPath: /tmp/planka-temp
    readOnly: false
    emptyDir:
      sizeLimit: 1Gi

  # Host path mount
  - name: backup-storage
    mountPath: /var/lib/planka-backups
    readOnly: false
    hostPath:
      path: /var/lib/planka-backups
      type: DirectoryOrCreate

  # NFS mount
  - name: nfs-storage
    mountPath: /shared/data
    readOnly: false
    nfs:
      server: nfs.example.com
      path: /exports/planka
```

### Environment Variables from Secrets

You can reference values from existing secrets in environment variables:

```yaml
extraEnv:
  - name: SMTP_PASSWORD
    valueFrom:
      secretName: smtp-credentials
      key: password
  - name: CUSTOM_API_KEY
    valueFrom:
      secretName: api-credentials
      key: api-key
```

### Custom Terms of Service

You can provide your own End User Terms of Service by passing the markdown files directly via `values.yaml` in the `terms` configuration block. This automates the creation of a corresponding ConfigMap and volume mount.

```yaml
terms:
  enabled: true
  customFiles:
    en-US.md: |
      # End User Terms of Service
      ...
      [confirmations]::
      ---
      ✔️ **I have read and accept these End User Terms of Service**
    de-DE.md: |
      # Nutzungsbedingungen
      ...
      [confirmations]::
      ---
      ✔️ **Ich habe diese Nutzungsbedingungen gelesen und akzeptiere sie**
```

### Image Digest Pinning

For enhanced security and reproducibility, you can pin the container image using its SHA256 digest instead of relying solely on tags. This ensures you always deploy the exact same image, preventing tag mutations or accidental updates.

#### Finding the Image Digest

You can find the digest of a specific image tag using:

```bash
docker inspect ghcr.io/plankanban/planka:latest --format='{{index .RepoDigests 0}}'
# Output: ghcr.io/plankanban/planka@sha256:abc123def456...

# Or with skopeo
skopeo inspect docker://ghcr.io/plankanban/planka:latest
```

#### Usage

You can use digest pinning in several ways:

**Option 1: Digest with tag (recommended)**

Includes the tag for reference while using the digest for verification:

```bash
helm install planka . --set secretkey=$SECRETKEY \
  --set image.tag=latest \
  --set image.digest=abc123def456... \
  --set admin_email="[email protected]" \
  --set admin_password="demo" \
  --set admin_name="Demo Demo" \
  --set admin_username="demo"
```

Or in values.yaml:

```yaml
image:
  repository: ghcr.io/plankanban/planka
  tag: latest
  digest: "abc123def456ab89cd12ef34ab56cd78ef90ab12cd34ef56ab78cd90ef12ab34"
```

**Option 2: Digest only**

If you prefer to pin only by digest without specifying a tag:

```yaml
image:
  repository: ghcr.io/plankanban/planka
  tag: "" # Empty - digest alone identifies the image
  digest: "abc123def456ab89cd12ef34ab56cd78ef90ab12cd34ef56ab78cd90ef12ab34"
```

#### Security Benefits

- **Immutability**: Ensures you always deploy the exact same image
- **Supply Chain Security**: Protects against tag mutations or registry compromise
- **Reproducibility**: Makes deployments fully reproducible across environments
- **Audit Trail**: Provides clear image identity in deployment manifests

--- METRICS ---