Skip to main content

Overview

Plane is an open-source project tracker, a Jira and Linear alternative: work items, cycles, modules, roadmaps and wiki pages with a real-time collaborative editor. This template deploys Plane Community Edition with its PostgreSQL, Redis, RabbitMQ and an object store for attachments (bundled, or your own AWS S3 or S3-compatible bucket). Plane runs in exactly one location of your GVC and installs private: you claim the instance in God Mode over a port-forward, then publish.
This template deploys into an existing GVC that you already have — it does not create or manage a GVC. The location you set must already be one of that GVC’s locations; if it is not, the Plane workloads start nothing.

What Gets Created

Your application keys, database, Redis, RabbitMQ and object store credentials are not created by the template — see Prerequisites.

Prerequisites

A GVC with one location, and these dictionary secrets created before you install. The database, Redis and RabbitMQ values go into connection URLs, so use letters, digits, -, _ and . only. Secret names are org-wide, so give each release its own.
1

Create the application keys secret

Set secrets.name to this name. SECRET_KEY signs sessions and encrypts the instance settings Plane stores (SMTP password, OAuth secrets), so back it up outside Control Plane and never replace it: replacing it signs everyone out and makes those settings unreadable.
2

Create the database credentials secret

Set postgres.config.credentialsSecretName to this name.
3

Create the Redis password secret

Set redis.credentialsSecretName to this name.
4

Create the RabbitMQ login secret

Set rabbitmq.credentialsSecretName to this name. RabbitMQ reads it on its first boot only.
5

Create the object store keys secret

Only with the default storage.type: minio. accessKey needs 3 or more characters and secretKey 8 or more:
Set storage.minio.credentialsSecretName to this name. For storage.type: s3 see Storage Setup instead.
Create every secret before installing. Installing without one reports success, but the deployment never becomes ready and cpln logs stays empty; status.versions[].message from the command below names the missing secret:
A secret created after install is not picked up on its own: see Rotating a Secret.

Installation

Once the secrets exist, install from the marketplace registry, pointing each key at its secret and location at a location of your GVC:
The first install applies the database migrations before the API turns ready, so allow several minutes. Other ways to install and manage the template:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

Location and Plane

The scheduler is always one replica and runs the database migrations, so beat.resources.maxCpu sets how fast they apply. Changing plane.appUrl changes the address every sign-in provider redirects to; see Authentication.

Storage

Datastores

Plane runs on PostgreSQL 15 and 16, so the bundled database stays on 16 and no highly available database is offered.

Access

The bundled PostgreSQL has its own postgres.internalAccess. With workload-list there, list //gvc/GVC_NAME/workload/RELEASE_NAME-plane, RELEASE_NAME-plane-worker and RELEASE_NAME-plane-beat in the same form.

Storage Setup

Only for storage.type: s3. Browsers upload to and download from the bucket directly, so it needs a CORS rule for your Plane origin (replace PLANE_URL with the instance URL, scheme included):
AWS S3 (keyless). Create the bucket and its CORS rule, create a Control Plane cloud account for AWS, and create this IAM policy (replace BUCKET_NAME). Set storage.s3.bucket, region, cloudAccountName and policyName (the IAM policy’s name); leave auth.secretName empty.
S3-compatible servers (MinIO, SeaweedFS, Google Cloud Storage interoperability with an HMAC key). Create the bucket and its CORS rule, store the keys, and set storage.s3.bucket, storage.s3.endpoint and storage.s3.auth.secretName to the secret:
Plane gives browsers presigned URLs built from endpoint, so it must be a full URL that users’ browsers can reach, such as a public HTTPS endpoint. An internal *.cpln.local address serves the API but not browser uploads and downloads.

Connecting

Plane has no bootstrap credential: whoever completes /god-mode first becomes the instance admin, so the install starts private.

First Run

  1. Install with publicAccess.enabled: false (the default) and wait for RELEASE_NAME-plane to become ready.
  2. Run cpln port-forward RELEASE_NAME-plane 8080:80 --gvc GVC_NAME, open localhost:8080/god-mode/ in a browser and create the instance admin. Then open localhost:8080 to create your workspace.
  3. Set up email and sign-in methods in God Mode (below) while the instance is still private.
  4. Publish with publicAccess.enabled: true; the public firewall can take a few minutes to open:

God Mode

God Mode (PLANE_URL/god-mode/) holds the instance settings. They are stored in the database, not in chart values, so they survive restarts and upgrades. Sign in with the admin you created in First Run.

Email

Under God Mode, Email, set the SMTP host, port, sender address and security. Email is required for invites, notifications and unique-code sign-in. A relay in the same GVC is reached at SMTP_WORKLOAD_NAME.GVC_NAME.cpln.local; set the security option to match what the relay speaks (none for a plain relay). The SMTP password is encrypted with SECRET_KEY.

Authentication

Under God Mode, Authentication:
  • Unique codes emails a sign-in code (magic link). It needs Email to be set up first.
  • Password sign-in with email and password.
  • Allow anyone to sign up is on by default: an unknown email address registers itself. Turn it off to limit access to invited users.
  • GitHub, Google and GitLab sign-in each need an OAuth app registered with the provider. God Mode shows the exact callback to register: PLANE_URL/auth/github/callback/, PLANE_URL/auth/google/callback/ or PLANE_URL/auth/gitlab/callback/, plus the origin URL for Google. Enter the client ID and secret in God Mode. Client secrets are stored encrypted.
The callback is built from the address the instance is served on. If the URL changes, for example when you set plane.appUrl to a custom domain, register the new callback with each provider.

General

Under God Mode, General, set the instance name, manage instance admins and turn telemetry off. Plane sends usage telemetry to telemetry.plane.so until you do.
SECRET_KEY encrypts the settings stored here. Never replace it, or the saved SMTP password and OAuth secrets become unreadable.

Webhooks and Exports

  • Webhooks (workspace settings, Webhooks) must point at a public URL; internal and private addresses, including *.cpln.local workloads, are refused. Plane generates each webhook’s signing secret and signs the request body in the x-plane-signature header (HMAC-SHA256, hex, keyed with that secret).
  • Exports appear as download links under workspace settings, Exports. No email is sent.
  • Uploads larger than plane.fileSizeLimit are refused with HTTP 413. The limit counts the whole upload request, so the largest usable file is a couple of kilobytes smaller.

Operations

Backing Up

The bundled PostgreSQL has no scheduled backups here, because Plane runs on PostgreSQL 15 and 16 and the template backups need PostgreSQL 17. Dump the database with pg_dump from the postgresql container, and back up the object store too: attachments live there, not in the database.
Use the username and database values from the database credentials secret. For storage.type: s3, back up the bucket with your provider’s tools; for the bundled store, snapshot its volume set:
This template provides no restore procedure.

Rotating a Secret

Secrets are read when a replica starts, and a paused deployment does not retry on its own. After creating a missing prerequisite secret late, changing one, or an upgrade that changes which secrets the release reads, run cpln workload force-redeployment with --gvc GVC_NAME on RELEASE_NAME-plane, RELEASE_NAME-plane-worker, RELEASE_NAME-plane-beat and the datastore whose secret it is (RELEASE_NAME-postgres, RELEASE_NAME-plane-redis, RELEASE_NAME-plane-mq or RELEASE_NAME-plane-minio):
RabbitMQ creates its user on first boot only, and PostgreSQL its user on first initialization, so changing those credentials in the secret alone does not change the login inside the datastore. Never replace SECRET_KEY.

Scaling and Availability

plane.replicas and worker.replicas scale freely; with two or more plane.replicas the UI stays up through a rolling restart. The scheduler is always one replica. PostgreSQL, Redis, RabbitMQ and the object store are single instances.

Troubleshooting

Symptom: cpln helm install reported success, cpln logs returns nothing, and cpln workload get-deployments RELEASE_NAME-plane --gvc GVC_NAME -o yaml names a secret under status.versions[].message.Cause: A prerequisite secret does not exist.Fix: Create it (see Prerequisites), then force a redeployment as in Rotating a Secret.
Symptom: The deployment message reads deactivated because maxScale is set to 0 and nothing is logged.Cause: location is not one of the GVC’s locations.Fix: Set location to a location your GVC already has and run cpln helm upgrade with your install values.
Symptom: RELEASE_NAME-plane is not ready and the api container logs Waiting for database migrations to complete....Cause: RELEASE_NAME-plane-beat applies the migrations on its first start and the API waits for it.Fix: Wait for the scheduler to finish; follow its progress with the command below. If the message never clears, the scheduler is not running: check it and its database credentials.
Symptom: RELEASE_NAME-plane-beat logs password authentication failed and the API and worker stay unready.Cause: The password in the credentials secret no longer matches the one PostgreSQL initialized with.Fix: Restore the original password in the secret and force a redeployment of RELEASE_NAME-plane, RELEASE_NAME-plane-worker and RELEASE_NAME-plane-beat.
Symptom: The UI reports success but nothing is delivered.Cause: The worker is not running or is out of memory.Fix: Check RELEASE_NAME-plane-worker, and raise worker.resources.maxMemory together with worker.concurrency.
Symptom: An attachment upload fails with 413 and no message.Cause: The file exceeds plane.fileSizeLimit.Fix: Raise plane.fileSizeLimit (bytes) and run cpln helm upgrade with your install values.
Symptom: cpln helm install fails with a message beginning plane:. The messages and their fixes:
  • plane: `location` is required, plane: `location` must be a single location NAME and plane: `location` must be a bare location NAME like aws-us-east-1: location is empty, a list, or a link form such as //location/aws-us-east-1.
  • plane: `locations` (plural) is not a key of this chart: use the singular location.
  • plane: secrets.name is required, plane: redis.credentialsSecretName is required, plane: rabbitmq.credentialsSecretName is required, plane: postgres.config.credentialsSecretName is required and plane: storage.minio.credentialsSecretName is required with storage.type minio: a prerequisite secret name is empty.
  • plane: postgres.credentials is not a key of this chart: database credentials are a secret, not values.
  • plane: beat.replicas is not a key of this chart: the scheduler is always one replica.
  • plane: plane.replicas must be at least 1, plane: worker.replicas must be at least 1, plane: worker.concurrency must be at least 1 and plane: plane.api.gunicornWorkers must be at least 1: raise the value.
  • plane: plane.appUrl must start with https:// or http://: add the scheme.
  • plane: storage.type must be 'minio' or 's3', plane: storage.s3.bucket is required when storage.type is s3, plane: s3 storage needs credentials and plane: static keys (storage.s3.auth.secretName) are only for S3-compatible servers: AWS S3 uses cloudAccountName and policyName; an S3-compatible server uses endpoint and auth.secretName.
  • plane: internalAccess.type `none` would cut the API, worker and scheduler off and plane: postgres.internalAccess.type must not be 'none': use same-gvc or workload-list.
  • plane: internalAccess.workloads may only be set when internalAccess.type is `workload-list` : remove the list or switch the type.
  • plane: postgres.internalAccess.type is 'workload-list' but the list does not include: add RELEASE_NAME-plane, RELEASE_NAME-plane-worker and RELEASE_NAME-plane-beat.
Cause: A value is missing or invalid.Fix: Correct the value the message names and reinstall.

Important Notes

  • Install into a GVC with one location and set location to it; the bundled PostgreSQL is not pinned to location and starts a copy in every other location of the GVC.
  • Claim /god-mode privately first: the first visitor becomes the instance admin.
  • Never replace SECRET_KEY: it encrypts the stored SMTP and OAuth settings.
  • Re-register OAuth callbacks whenever the instance URL changes.
  • An S3-compatible storage.s3.endpoint must be reachable from users’ browsers, and the bucket needs the CORS rule above.
  • No highly available database and no scheduled backups: dump the database with pg_dump and back up the object store yourself.
  • Turn telemetry off in God Mode, General, if you do not want Plane to report usage.
  • Plane’s integrations service, Intake and Monitor are not part of the Community Edition.

External References

Plane

Product site

Self-hosting overview

Upstream self-hosting documentation

Plane architecture

How Plane’s services fit together

REST API reference

Personal API tokens and endpoints

Plane on GitHub

Source code and releases