Deploy Plane Airgapped on Docker Enterprise Grid
INFO
Airgapped deployments are available exclusively for Enterprise Grid customers with a minimum commitment of 100 seats. Contact our Sales team for trials, exceptions to the seat cut-off, tailored pricing, and licensing info.
This guide walks you through deploying Plane Commercial in an airgapped Docker environment using Docker Compose and pre-configured images from your private registry.
Prerequisites
Before starting, ensure you have:
- Docker (version 24 or later) installed and running
- Docker Compose Plugin installed (you should be able to run
docker composeordocker-compose) - Access to a private Docker registry containing Plane images
- Required ports opened to access the application (80, 443)
WARNING
While Docker can run stateful services with persistent volumes, we strongly recommend using external managed services for better reliability in backup/restore operations and disaster recovery.
Consider these alternatives:
- MinIO: Replace with AWS S3, Google Cloud Storage, or any S3-compatible service
- Redis: Replace with Valkey or a managed Redis service
- PostgreSQL: Use a managed PostgreSQL service
- RabbitMQ: Use a managed message queue service
- OpenSearch: Use a managed OpenSearch service
Install Plane
Prepare Docker images for airgapped environment
Refer to this document to download the Docker images from the Plane artifact registry to your internal registry.
INFO
This process will NOT download or clone these infrastructure images:
valkey/valkey:7.2.11-alpinepostgres:15.7-alpinerabbitmq:3.13.6-management-alpinedocker.io/pgsty/minio:RELEASE.2026-08-04T00-00-00Zdocker.io/pgsty/mc:RELEASE.2026-09-16T00-00-00Zopensearchproject/opensearch:3.3.2
If you're using local infrastructure services, you'll need to pull and transfer these images separately.
Download Docker Compose configuration
bash# Download docker-compose.yml curl -fsSL https://prime.plane.so/releases/<plane-version>/docker-compose-airgapped.yml -o docker-compose.yml # Download environment template curl -fsSL https://prime.plane.so/releases/<plane-version>/variables-airgapped.env -o plane.envConfigure environment variables
Edit the
plane.envfile to configure your deployment:bash# Set your domain DOMAIN_NAME=plane.yourcompany.com WEB_URL=https://plane.yourcompany.com CORS_ALLOWED_ORIGINS=https://plane.yourcompany.com SITE_ADDRESS=https://plane.yourcompany.com # Paste the generated UUID here (see note below) MACHINE_SIGNATURE=your-uuid-hereINFO
Generating
MACHINE_SIGNATURE: Run the following command in your terminal to generate a unique UUID, then paste the output as the value ofMACHINE_SIGNATUREinplane.env:bashuuidgenUpdate image references in
docker-compose.ymlto point to your private registry:yamlservices: web: image: your-registry.io/plane/web-commercial:${APP_RELEASE_VERSION} api: image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} worker: image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} beat-worker: image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} migrator: image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} importer-worker: image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} automation-consumer: image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} webhook-consumer: image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} outbox-poller: image: your-registry.io/plane/backend-commercial:${APP_RELEASE_VERSION} space: image: your-registry.io/plane/space-commercial:${APP_RELEASE_VERSION} admin: image: your-registry.io/plane/admin-commercial:${APP_RELEASE_VERSION} live: image: your-registry.io/plane/live-commercial:${APP_RELEASE_VERSION} live-exporter: image: your-registry.io/plane/live-commercial:${APP_RELEASE_VERSION} monitor: image: your-registry.io/plane/monitor-commercial:${APP_RELEASE_VERSION} silo: image: your-registry.io/plane/silo-commercial:${APP_RELEASE_VERSION} email: image: your-registry.io/plane/email-commercial:${APP_RELEASE_VERSION} pi-api: image: your-registry.io/plane/plane-pi-commercial:${APP_RELEASE_VERSION} pi-beat: image: your-registry.io/plane/plane-pi-commercial:${APP_RELEASE_VERSION} pi-worker: image: your-registry.io/plane/plane-pi-commercial:${APP_RELEASE_VERSION} pi-migrator: image: your-registry.io/plane/plane-pi-commercial:${APP_RELEASE_VERSION} runner: image: your-registry.io/plane/node-runner-commercial:${APP_RELEASE_VERSION} iframely: image: your-registry.io/plane/iframely:v2.5.3 proxy: image: your-registry.io/plane/proxy-commercial:${APP_RELEASE_VERSION}Infrastructure services (if using local setup):
yamlservices: plane-redis: image: valkey/valkey:7.2.11-alpine plane-db: image: postgres:15.7-alpine pi-db-init: image: postgres:15.7-alpine plane-mq: image: rabbitmq:3.13.6-management-alpine plane-minio: image: docker.io/pgsty/minio:RELEASE.2026-08-04T00-00-00Z
Start Plane
Start the services:
bashdocker compose --env-file plane.env up -dWatch the logs to make sure everything starts properly.
- To monitor the database migration process:
bashdocker compose logs -f migrator- To monitor the API service startup:
bashdocker compose logs -f apiThe API is healthy when you see:
api-1 listening atOnce all services are running smoothly, you can access Plane by opening your browser and going to the domain you configured.
You now have Plane running in your airgappedenvironment. If you run into any issues, check the logs using the commands above, or reach out to our support team for assistance.
Use your own SSL certificate
An airgapped host cannot reach Let's Encrypt, so Plane starts on plain HTTP. To serve HTTPS, use a certificate issued by your internal CA. Available in Plane v3.2.0 and later.
The proxy service mounts the ssl/ folder of your install directory read-only at /ssl. The install directory is the directory that holds docker-compose.yml and plane.env.
Check the proxy mount
Open
docker-compose.ymland look at thevolumesof theproxyservice. It must contain:yaml- ./ssl:/ssl:roOlder bundles ship
- ./data//path/to/ssl:/ssl:roinstead, which points nowhere. Replace that line with the one above.Place the certificate and key
bashcd /path/to/plane mkdir -p ssl cp fullchain.pem ssl/cert.pem # PEM, full chain (leaf plus intermediates) cp privkey.pem ssl/key.pem # PEM private key matching cert.pem chmod 600 ssl/key.pemThe certificate must cover the domain you set in
plane.env, and be issued by a CA your users' browsers trust.Switch
plane.envto HTTPSbashSITE_ADDRESS=plane.yourcompany.com APP_PROTOCOL=https WEB_URL=https://plane.yourcompany.com CORS_ALLOWED_ORIGINS=https://plane.yourcompany.comIf
SITE_ADDRESSis left as a plain-HTTP address (localhost:80,:80, or anhttp://URL) the certificate is ignored with a warning.Recreate the proxy
bashdocker compose --env-file plane.env up -d --force-recreate proxyIf you also changed
WEB_URLorCORS_ALLOWED_ORIGINS, recreate the services that read them:bashdocker compose --env-file plane.env up -d --force-recreate api web space admin liveVerify
bashdocker compose --env-file plane.env logs proxy | grep -i "ssl certificate"Expect
Custom SSL certificate enabled: /ssl/cert.pem (key: /ssl/key.pem). If you seeWARNING: custom SSL certificate ignored:instead, the reason follows on the same line. Then openhttps://plane.yourcompany.comfrom a machine that trusts your CA.
To rotate the certificate, replace both files in ssl/ and recreate the proxy. The ssl/ folder is kept across upgrades. For file formats, permissions and the full troubleshooting table, see Use your own certificate.

