Skip to content

How to Deploy Plane on a VPS

Updated Sep 2026

verified on Ubuntu 26.04 · Sep 2026
We earn commissions when you shop through the links below. Full disclosure →

Self-host Plane Community Edition on a VPS with the official setup script — pinned release, generated secrets, HTTPS through Caddy, and a real backup routine.

Before you start
  • A VPS with 2 vCPU and at least 4 GB RAM (8 GB recommended by upstream for production)
  • A fresh Ubuntu 24.04 or 26.04 server with root/sudo SSH access
  • A domain or subdomain you can point at the server
  • Docker Engine + Compose installed (see the base guide below)
Need a box for this guide? Kamatera's free tier lets you spin one up now.Start free on Kamatera → (opens in new tab)

What Plane is

Plane is an open-source project management platform for tracking work items, running sprint cycles and managing product roadmaps. Work items can be shown as a list, kanban board, calendar, spreadsheet-style table or timeline (a Gantt equivalent), with cycles, modules, saved views and wiki-style pages on top. It is written in TypeScript and Python, runs on PostgreSQL and Redis, is licensed AGPL-3.0 for the Community Edition, and is rated 3 / 5 to deploy.

This guide installs the Community Edition. Some AI and enterprise features shown on Plane's hosted cloud are paid-plan features and are not part of it. Upstream's docs now lead with a "Prime CLI" installer for the Commercial Edition; the Community Edition still uses the setup.sh script this guide walks through.

If you are weighing Plane against the other big open-source option, read Plane vs OpenProject first — or see Deploy OpenProject on a VPS.

Server sizing

Plane is a stack, not a single app: a web frontend, the public "space" app, an admin app, a live-collaboration server, an API, background workers, Postgres, Redis, RabbitMQ, MinIO for uploads and a bundled proxy — around a dozen containers.

  • Upstream minimum: 2 CPU cores and 4 GB RAM, with 8 GB recommended for production.
  • Measured: on a GCP e2-standard-2 running Ubuntu 26.04 with Docker 29.8.1, the idle stack used ~1.1 GB of RAM and ~4.5 GB of disk for images and data (September 2026).

The idle number is well under the 4 GB floor, but it is idle: workers, migrations and a team using the app concurrently push memory up. Do not try to fit Plane on a 2 GB box, and give it a 40 GB+ disk so uploads and image updates have room.

Prepare the server

This guide assumes Docker Engine and the Compose plugin are installed, along with a non-root deploy user and a ufw firewall. If not, work through Docker & Compose on Ubuntu first.

sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
sudo ufw status verbose

One caveat worth knowing before the install: ports that Docker publishes bypass ufw. Plane's bundled proxy will publish port 8080 below so that Caddy can reach it; block 8080 in your provider's network firewall too if you don't want it reachable directly.

Where to host itaffiliate disclosure
Hetzner Cloudrun it on
2 vCPU · 4 GB RAM · 80 GB SSD · $23.59/mo
Get Hetzner Cloud (opens in new tab)
Kamaterafree trial
1 vCPU · 1 GB RAM · 20 GB SSD · $4.00/mo
Start free on Kamatera → (opens in new tab)
DigitalOceanalso works on
1 vCPU · 1 GB RAM · 25 GB SSD · $6.00/mo
Deploy on DigitalOcean → (opens in new tab)

Paid link — we earn a commission if you shop through it.

Install Plane (Community Edition)

Create a working directory and download the official setup script. Upstream's instructions use the latest release asset; this guide pins v1.4.2, the release the catalog was verified with. The script's default stable channel looks the latest version up through the GitHub API, and that lookup broke against GitHub's current compact JSON output, so the pin also avoids that:

mkdir -p ~/plane-selfhost && cd ~/plane-selfhost
curl -fsSL -o setup.sh https://github.com/makeplane/plane/releases/download/v1.4.2/setup.sh
chmod +x setup.sh
sed -i 's/^export APP_RELEASE=stable$/export APP_RELEASE=v1.4.2/' setup.sh

Run the install action. It creates plane-app/ with the release's docker-compose.yaml and a plane.env file of settings, and checks that the images exist for your CPU architecture:

cd ~/plane-selfhost
[ -f plane-app/plane.env ] || ./setup.sh install
ls plane-app

Configure plane.env

Before the first start, change four groups of settings in plane-app/plane.env:

  • Ports: move the bundled proxy off 80/443 (LISTEN_HTTP_PORT, LISTEN_HTTPS_PORT) so Caddy can own them.
  • URLs: WEB_URL and CORS_ALLOWED_ORIGINS must be your public HTTPS URL. Plane builds redirects, emails and API responses from WEB_URL.
  • Secrets: SECRET_KEY and LIVE_SERVER_SECRET_KEY ship as change-this-key-on-deployment. Replace them.
  • MinIO: AWS_SECRET_ACCESS_KEY is also the MinIO root password, and it ships as secret-key.
cd ~/plane-selfhost/plane-app
sed -i \
  -e 's|^LISTEN_HTTP_PORT=.*|LISTEN_HTTP_PORT=8080|' \
  -e 's|^LISTEN_HTTPS_PORT=.*|LISTEN_HTTPS_PORT=8443|' \
  -e 's|^APP_DOMAIN=.*|APP_DOMAIN=plane.example.com|' \
  -e 's|^WEB_URL=.*|WEB_URL=https://plane.example.com|' \
  -e 's|^CORS_ALLOWED_ORIGINS=.*|CORS_ALLOWED_ORIGINS=https://plane.example.com|' \
  plane.env
if grep -q '^SECRET_KEY=change-this-key-on-deployment' plane.env; then
  sed -i \
    -e "s|^SECRET_KEY=.*|SECRET_KEY=$(openssl rand -hex 32)|" \
    -e "s|^LIVE_SERVER_SECRET_KEY=.*|LIVE_SERVER_SECRET_KEY=$(openssl rand -hex 32)|" \
    -e "s|^AWS_SECRET_ACCESS_KEY=.*|AWS_SECRET_ACCESS_KEY=$(openssl rand -hex 24)|" \
    plane.env
fi
chmod 600 plane.env
grep -E '^(LISTEN_HTTP_PORT|LISTEN_HTTPS_PORT|WEB_URL|CORS_ALLOWED_ORIGINS)=' plane.env

Replace plane.example.com with your domain. The Postgres and RabbitMQ passwords stay at their defaults here: those services are only reachable on the stack's internal Docker network, and the compose file's default DATABASE_URL and AMQP_URL are built from those same defaults — change them only together.

Start it

cd ~/plane-selfhost
./setup.sh start
timeout 600 bash -c 'until [ "$(curl -so /dev/null -w "%{http_code}" http://127.0.0.1:8080/)" = "200" ]; do sleep 5; done'
curl -so /dev/null -w 'HTTP %{http_code}\n' http://127.0.0.1:8080/god-mode/
docker compose -f plane-app/docker-compose.yaml --env-file plane-app/plane.env ps --format '{{.Service}}\t{{.Status}}'

The first start pulls every image and runs the database migrations in a one-off migrator container, so it takes a few minutes. The migrator exits when it finishes, so it drops out of the list; everything else should be up.

HTTPS + domain

Point an A record for plane.example.com at the server's public IP, wait for it to resolve, then put Caddy in front of the bundled proxy on port 8080:

plane.example.com {
    reverse_proxy 127.0.0.1:8080
}

The bundled proxy already routes /api, /auth, /god-mode, /spaces, /live and the uploads bucket to the right containers, so one upstream is all Caddy needs, and Caddy passes the WebSocket upgrade the live-collaboration server uses without extra configuration.

Two alternatives upstream documents, if you'd rather not run a second proxy:

  • The bundled proxy can request its own Let's Encrypt certificate. Set SITE_ADDRESS to the bare domain, CERT_EMAIL to your email and WEB_URL to the https:// URL, and leave the listen ports on 80/443.
  • Or remove the bundled proxy service and route each path to its container yourself — more work, and only worth it if you already run a proxy on the Docker network.

First login and the instance admin

Open https://plane.example.com/god-mode/ first. That is the instance admin panel, and the first account you create there becomes the instance admin. Do it right after the first start. From there you control:

  • Sign-up: whether anyone can create an account, or only invited users.
  • Authentication: email/password, magic links, and Google, GitHub or GitLab OAuth.
  • Email (SMTP): needed for invitations and magic links.

Then open the main URL, create a workspace, and invite your team.

Backups

Plane's data lives in Postgres (everything except files), MinIO (uploads) and, less critically, Redis and RabbitMQ. The setup script's backup action copies all four volumes into plane-app/backup/<timestamp>/, and upstream provides a matching restore.sh:

cd ~/plane-selfhost
./setup.sh backup
ls plane-app/backup/

That action copies Postgres's data directory while the database is running. For a transactionally consistent copy of the part that matters most, take a logical dump as well. The database container sets PGHOST, so pg_dump connects over TCP and needs the password from the container's own environment:

cd ~/plane-selfhost/plane-app
mkdir -p backup
set -o pipefail
docker compose --env-file plane.env exec -T plane-db sh -c 'PGPASSWORD="$POSTGRES_PASSWORD" pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' | gzip > backup/plane-db-$(date +%F).sql.gz
ls -lh backup/*.sql.gz

Copy the backup folder and plane.env off the server — upstream calls out backing up plane.env separately, because it holds the secrets.

Upgrades

Back up first. The setup script's upgrade action stops the stack, downloads the newer compose file and plane.env template, and carries your existing settings over. It asks for confirmation, so run it interactively:

cd ~/plane-selfhost
./setup.sh upgrade
./setup.sh start

Because setup.sh itself is pinned to v1.4.2 here, download the newer release's setup.sh and set its APP_RELEASE line to that version before you upgrade, the same way you installed it. Read Plane's release notes before jumping several versions.

Troubleshooting

The migrator container exited with an error. Read its logs with docker compose -f plane-app/docker-compose.yaml --env-file plane-app/plane.env logs migrator. A database that wasn't ready is the usual cause; ./setup.sh restart re-runs it.

The page loads but API calls fail with CORS errors. WEB_URL and CORS_ALLOWED_ORIGINS don't match the URL in your browser. Fix both in plane.env and run ./setup.sh restart.

Uploads fail. Uploads go through the bundled proxy to MinIO. The default FILE_SIZE_LIMIT is 5 MB (5242880 bytes); raise it in plane.env if you need larger files.

Port 80 or 443 already in use. Something else, often a host Caddy or nginx, holds the port. That is why this guide moves the bundled proxy to 8080/8443.

Verification + next steps

You're done when you can: load https://plane.example.com over a valid certificate, sign in to /god-mode/ as the instance admin, restrict sign-ups, create a workspace and a project, upload an attachment to a work item, and find last night's backup off the server.

From there: configure SMTP, connect Google or GitHub sign-in, and consider moving Postgres and uploads to managed services, which upstream recommends for production. For something much lighter, Vikunja and Leantime run in a fraction of the memory. For hosting options, see Best VPS for Docker.

Next steps

How to self-host Plane →More self-hosted project management tools →Automatic HTTPS with Caddy →Run Claude Code with Ollama on Your Own VPS →Deploy Coolify on a VPS →How to Deploy Actual Budget on a VPS →How to Deploy AnythingLLM on a VPS →How to Deploy Appwrite on a VPS →How to Deploy Audiobookshelf on a VPS →How to Deploy Authelia on a VPS →How to Deploy authentik on a VPS →How to Deploy Baserow on a VPS →How to Deploy Beszel on a VPS →How to Deploy Bitwarden on a VPS →How to Deploy BookStack on a VPS →How to Deploy CapRover on a VPS →How to Deploy Checkmate on a VPS →How to Deploy Directus on a VPS →How to Deploy docker-mailserver on a VPS →How to Deploy Docmost on a VPS →How to Deploy Dokku on a VPS →How to Deploy Dokploy on a VPS →How to Deploy Firefly III on a VPS →How to Deploy Forgejo on a VPS →How to Deploy Gatus on a VPS →How to Deploy Ghostfolio on a VPS →How to Deploy Gitea on a VPS →How to Deploy GitLab on a VPS →How to Deploy GlitchTip on a VPS →How to Deploy Grafana on a VPS →How to Deploy Graylog on a VPS →How to Deploy Headscale on a VPS →How to Deploy Healthchecks on a VPS →How to Deploy Home Assistant on a VPS →How to Deploy Immich on a VPS →How to Deploy Jan on a VPS →How to Deploy Jellyfin on a VPS →How to Deploy Karakeep on a VPS →How to Deploy Keycloak on a VPS →How to Deploy Leantime on a VPS →How to Deploy LibreChat on a VPS →How to Deploy Linkwarden on a VPS →How to Deploy LocalAI on a VPS →How to Deploy Mailcow on a VPS →How to Deploy Mailu on a VPS →How to Deploy Matomo on a VPS →How to Deploy Mattermost on a VPS →How to Deploy Meilisearch on a VPS →How to Deploy Memos on a VPS →How to Deploy n8n on a VPS →How to Deploy Navidrome on a VPS →How to Deploy NetBird on a VPS →How to Deploy Netdata on a VPS →How to Deploy Nextcloud on a VPS →How to Deploy Next.js to a VPS →How to Deploy Nginx Proxy Manager on a VPS →How to Deploy NocoDB on a VPS →How to Deploy ntfy on a VPS →How to Deploy Ollama on a VPS →How to Deploy Open WebUI on a VPS →How to Deploy OpenHands on a VPS →How to Deploy OpenObserve on a VPS →How to Deploy OpenProject on a VPS →How to Deploy Outline on a VPS →How to Deploy Pangolin on a VPS →How to Deploy Paperless-ngx on a VPS →How to Deploy Passbolt on a VPS →How to Deploy Plausible Analytics on a VPS →How to Deploy Pocket ID on a VPS →How to Deploy PocketBase on a VPS →How to Deploy Prometheus on a VPS →How to Deploy Psono on a VPS →How to Deploy Radarr on a VPS →How to Deploy Rocket.Chat on a VPS →How to Deploy SigNoz on a VPS →How to Deploy Sonarr on a VPS →How to Deploy Stalwart on a VPS →How to Deploy Stirling-PDF on a VPS →How to Deploy Supabase on a VPS →How to Deploy Synapse on a VPS →How to Deploy Taiga on a VPS →How to Deploy TeamPass on a VPS →How to Deploy Tinyauth on a VPS →How to Deploy Traefik on a VPS →How to Deploy Trilium on a VPS →How to Deploy Twenty CRM on a VPS →How to Deploy Umami on a VPS →How to Deploy Uptime Kuma on a VPS →How to Deploy Vaultwarden on a VPS →How to Deploy Vikunja on a VPS →How to Deploy wg-easy on a VPS →How to Deploy Wiki.js on a VPS →How to Deploy Zabbix on a VPS →How to Deploy Zitadel on a VPS →How to Deploy Zulip on a VPS →Docker & Compose on Ubuntu 26.04 →Building AI Workflows with n8n →Install Open WebUI with Ollama →Adding AI-Powered Insights to Plausible Analytics →Building AI-Powered Apps with Supabase and pgvector →

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.