Skip to main content

Plane on Google Cloud Run

Plane on Google Cloud Run

Plane is an open-source project-management platform — a Jira / Linear alternative for issues, cycles, modules, and roadmaps. This module deploys Plane on Cloud Run v2 on top of the App_CloudRun foundation, which provisions and manages the shared Google Cloud infrastructure.

This guide focuses on the cloud services Plane uses and how to explore and operate them from the Google Cloud Console and the command line. For the mechanics common to every Cloud Run application — service identity, ingress and load balancing, scaling and concurrency, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC Service Controls, backups, and the deployment lifecycle — refer to the App_CloudRun foundation guide rather than repeating them here.


What Plane costs on RAD, and how that compares​

Running Plane in your own Google Cloud project costs about US$109.88/month in infrastructure, plus a one-time module fee of 175 RAD credits (sized at 2 vCPU / 4 GiB on an always-on warm Cloud Run instance). RAD charges the module fee once, at deploy time — never again on Update — plus build time metered in credits; Google Cloud usage itself is billed to your own billing account at Google's list price. On a RAD-managed project the same resources are metered hourly in credits at list price plus RAD's margin — about 49 credits/day — and the module fee is 10% lower, at 157.50 credits. See "Pause it for free" below for how a RAD-managed Plane deployment can be stopped, at no running cost, until you need it again. (or see the GKE guide if you need Kubernetes)

What you pay on RAD​

ItemOwn projectRAD-managed project
Module fee175 credits (once)157.50 credits (once)
Build time~3-6 credits, metered per build minutesame, metered in credits at list price + RAD's margin
Google Cloud running cost~$109.88/month, billed to your own billing account at Google's list price~49 credits/day, metered hourly at list price + RAD's margin

Default running cost in your own project

ResourceUS$/month (Google list price, us-central1)
Cloud Run, 2x vCPU / 4 GiB (warm)39.43
Cloud SQL for PostgreSQL, 1 vCPU / 3.75 GB, zonal, 10 GB SSD51.02
NFS/cache file server (small VM)13.43
Cloud NAT and networking5
Cloud Storage (add-ons, backups)1
Total$109.88

How it compares​

  • Plane's own creators also sell Plane Cloud, whose cheapest paid tier (Pro) is $8/user/month ($6/user/month billed annually) — verified on their pricing page 8 October 2026. A 14-person team would pay more per month in Plane Cloud seats alone than this deployment's entire infrastructure bill, with no seat limit; a 2-3 person team pays less on Plane Cloud. Self-hosting trades the per-seat ceiling for one flat infrastructure cost that does not grow with headcount.
  • On a bare VPS you are the one applying OS/database security patches, taking and testing backups, and renewing certificates; RAD's managed Cloud SQL, Secret Manager and monitoring handle all three for you.

Pause it for free: delete a RAD-managed project, restore it when you need it​

This is the headline option on a RAD-managed project, and it is free while paused. Deleting a RAD-managed project first unlinks its billing, then asks Google to delete the project itself — Google's own 30-day recoverable soft delete. Unlike deleting a single module, this does not tear down the database, any VM, or the compute resource one at a time: the whole project simply stops, and because billing is already unlinked, nothing is charged while it waits.

Restoring — within 30 days, and only by the project's owner — asks Google to undelete the project and reattaches its billing account, then asks you to run Update on each deployment to confirm everything came back. Because nothing was individually destroyed, that Update finds the same resources already there: it is a check, not a rebuild, and an Update never charges the module fee again. The whole restore costs only a handful of credits (under US$1) in build time for a typical chain of deployments. What this needs: you must own the project (not merely have RAD manage its billing), you must restore it yourself within the 30 days — after that Google deletes it for good — and restoring is admitted like creating a new project, so your purchased credit balance must still clear the tier's floor (100 credits for the sandbox tier most study and demo use fits). Google says most services are fully working again within 36 hours of a restore.

One real gap: nightly backups are written to a bucket inside the project, and that bucket has Cloud Storage's soft-delete explicitly turned off, so the backup bucket is very likely gone as soon as you delete the project — even though the project itself is recoverable for 30 days. If you have customised this deployment and want to keep that work, copy a backup out (to Google Drive, or a bucket outside the project) before deleting. For a default install with nothing irreplaceable in it, this does not matter.

Pay only while you use it, the other way: delete and redeploy​

The other lever, for an own project (or a RAD-managed one past its 30-day restore window), is to delete Plane outright and redeploy it later. A redeploy costs the module fee again plus the builds — roughly 178-181 credits in total for this module.

Deleting only saves money once Plane would otherwise sit unused long enough to clear that redeploy cost against its own running cost — for this module, that is about 5 days or more of being idle, based on its own US$109.88/month running cost above. Most of that running cost is usually the database and any shared file/cache VM, and they stop only once nothing else in the project uses them, so deleting this app alone saves only its own compute share if something else shares the project.

Keep data first: nightly backups go to a bucket inside the deployment and are deleted with it, so copy the latest backup out before deleting if you want to keep it.

Lab sessions and Managed Environments​

  • Lab sessions, for training: a trainer runs a session for a class. Each participant gets the app in their own Google Cloud project for 15 minutes to 24 hours, within an allowance the trainer sets. Either the trainer funds every place, or each participant pays for their own. Everything is deleted when the session ends and unused credits go back to the trainer.
  • Managed Environments, for consultancies: a partner runs the app for a client from a ring-fenced wallet it funds, and settles with the client directly. If the wallet runs low, billing pauses and the data is kept, so nobody receives an unexpected charge. At the end the partner hands the project over and the deployments become the client's own.
  • Credits can be bought in more than 20 currencies, including XAF, XOF, NGN, GHS, KES and ZAR, by card, bank transfer or mobile money.

Sources (8 October 2026): Google Cloud Billing Catalog API list prices; radmodules.dev/pricing for RAD's own fees and daily-credit estimates; Resource Manager: delete and restore projects for the pause/restore mechanics. https://plane.so/pricing

1. Overview​

Plane's upstream self-host stack is multi-service: web / space / admin (frontends), api (Django/gunicorn), worker + beat (Celery), live (real-time collaboration), and a migrator job, plus PostgreSQL, Redis, RabbitMQ, and S3-compatible object storage. This module deploys Plane's published all-in-one community image (makeplane/plane-aio-community), which bundles every sub-service behind an internal Caddy reverse proxy on port 80 via supervisord — so one Cloud Run service exposes the whole app. The deployment wires together:

CapabilityGoogle Cloud serviceNotes
ComputeCloud Run v2All-in-one container (api + workers + frontends + migrator), 2 vCPU / 4 GiB by default
Message brokerRabbitMQ in-pod sidecarrabbitmq:3.13-management-alpine at 127.0.0.1:5672 — mandatory for Celery
DatabaseCloud SQL for PostgreSQL 15Plain PostgreSQL, no extensions required
Cache / task queue backendRedisEnabled by default; co-hosted on the NFS server VM when no external host is given
Shared filesFilestore / self-managed NFSRequired for the Redis co-location (gen2 execution environment)
Object storageCloud StorageA dedicated storage bucket is provisioned — S3 upload wiring is a documented TODO
SecretsSecret ManagerSECRET_KEY, LIVE_SERVER_SECRET_KEY, and the DB password managed automatically
IngressCloud Run URL / Cloud Load BalancingDefault run.app URL, optional external HTTPS load balancer + custom domain

Sensible defaults worth knowing up front:

  • PostgreSQL 15 is the fixed engine. Plane is a Django application; database_type = "POSTGRES_15" and no other engine works.
  • RabbitMQ is mandatory and runs as an in-pod sidecar. Plane's start.sh exits non-zero if AMQP_URL is empty. AMQP (TCP 5672) is a non-HTTP protocol that Cloud Run service-to-service networking cannot carry, so the broker shares the pod at 127.0.0.1:5672. Broker state is ephemeral (queue durability is a documented TODO).
  • Custom build. A thin wrapper Dockerfile layers a platform entrypoint on the AIO image; the entrypoint composes the DATABASE_URL / REDIS_URL / AMQP_URL connection strings Plane expects from the discrete DB_* / REDIS_* / RABBITMQ_* values the foundation injects.
  • application_version defaults to stable. The upstream image has no latest tag — a latest input is automatically mapped to stable at build time.
  • One instance kept alive, CPU on request. min_instance_count = 1 by default, because the instance also runs Plane's Celery worker and beat — at 0 it is reaped about 15 minutes after the last request and every periodic task stops. cpu_always_allocated = false (request-based billing) still throttles that instance's CPU between requests, so set cpu_always_allocated = true for continuous background processing.
  • Two application secrets are auto-generated in Secret Manager: Django SECRET_KEY (50 chars) and LIVE_SERVER_SECRET_KEY (40 chars, real-time collaboration auth).
  • A db-init job runs on every apply to idempotently create the Plane database and user; schema migrations are run by the AIO image's own migrator step on startup.
  • File uploads are a TODO. Plane needs an S3-compatible endpoint; the GCS storage bucket exists but GCS S3-interop HMAC keys are not yet wired. Issues, projects, and cycles work without it — attachments and avatars do not.
  • Health probes target /health through the internal Caddy proxy on port 80.

2. Google Cloud Services & How to Explore Them​

All commands assume PROJECT and REGION are set. Service and resource names are reported in the deployment Outputs.

A. Cloud Run — the Plane service (with the RabbitMQ sidecar)​

Plane runs as a single Cloud Run v2 service. The main container is the all-in-one image (supervisord runs migrator → api / frontends / live + Celery worker/beat behind Caddy on :80); a second mq sidecar container runs RabbitMQ. The main container's startup waits on the sidecar's TCP 5672 probe.

  • Console: Cloud Run → select the service for revisions, traffic, logs, and metrics. The sidecar appears under the revision's Containers tab.
  • CLI:
    gcloud run services list --project "$PROJECT" --region "$REGION"
    gcloud run services describe <service-name> --project "$PROJECT" --region "$REGION"
    gcloud run revisions list --service <service-name> --project "$PROJECT" --region "$REGION"

See App_CloudRun for scaling, concurrency, execution environment, and traffic splitting.

B. Cloud SQL for PostgreSQL 15​

Plane stores workspaces, projects, issues, cycles, and users in a managed Cloud SQL for PostgreSQL 15 instance. On first deploy a db-init Job creates the application database and user; the AIO image's migrator step then applies Django migrations on every container start. The platform entrypoint connects over the private TCP IP with sslmode=require (the Cloud SQL socket volume is mounted, but Plane's Django/psycopg uses the composed DATABASE_URL).

  • Console: SQL → select the instance for connections, backups, flags, metrics.
  • CLI:
    gcloud sql instances list --project "$PROJECT"
    gcloud sql instances describe <instance-name> --project "$PROJECT"
    gcloud sql connect <instance-name> --user=<db-user> --project "$PROJECT"

The instance name, database, user, and password secret are in the Outputs. See App_CloudRun for the connection model, backups, and password rotation.

C. Redis (Celery backend and cache)​

Redis backs Plane's cache and Celery result store. When no external redis_host is configured, the NFS server VM co-hosts Redis and its IP is resolved at runtime (the entrypoint substitutes the $(NFS_SERVER_IP) placeholder before composing REDIS_URL).

  • Console: Memorystore → Redis (if using a managed instance); Compute Engine → VM instances (NFS/Redis VM).
  • CLI:
    redis-cli -h <redis-host> ping
    gcloud run services logs read <service-name> --project "$PROJECT" --region "$REGION" --limit 50 | grep "Composed REDIS_URL"

D. Cloud Storage​

A dedicated storage bucket (gcs-<service-name>-storage) is provisioned for Plane file uploads. Upload wiring is a TODO: Plane requires an S3-compatible endpoint, and while AWS_S3_ENDPOINT_URL points at https://storage.googleapis.com, the GCS S3-interoperability HMAC keys are not provisioned — supply AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY via environment_variables (or use an external S3 bucket) to enable attachments.

  • Console: Cloud Storage → Buckets.
  • CLI:
    gcloud storage buckets list --project "$PROJECT" --filter="name~plane"
    gcloud storage ls gs://<storage-bucket>/

E. Secret Manager​

Three secrets are managed automatically: the Django SECRET_KEY, the LIVE_SERVER_SECRET_KEY (real-time collaboration auth), and the Cloud SQL password. All are injected at runtime; plaintext never appears in configuration.

  • Console: Security → Secret Manager.
  • CLI:
    gcloud secrets list --project "$PROJECT" --filter="name~plane"
    gcloud secrets versions access latest --secret=<secret-name> --project "$PROJECT"

See App_CloudRun for injection and rotation details.

F. Networking & ingress​

The service is reachable at its run.app URL by default; the predicted URL is injected as WEB_URL / DOMAIN_NAME / CORS_ALLOWED_ORIGINS so OAuth redirects, CORS, and email links work out of the box. An external HTTPS load balancer with a custom domain, Cloud CDN, and Cloud Armor can be layered on — if you do, the domain must match those URL variables.

  • Console: Cloud Run (service URL); Network services → Load balancing.
  • CLI:
    gcloud run services describe <service-name> --region "$REGION" --format='value(status.url)'
    gcloud compute addresses list --project "$PROJECT"

See App_CloudRun.

G. Cloud Logging & Monitoring​

Container logs (including supervisord output from every bundled sub-service and the mq sidecar) flow to Cloud Logging; Cloud Run and Cloud SQL metrics flow to Cloud Monitoring, with an optional uptime check against /health (disabled by default) and optional alert policies.

  • Console: Logging → Logs Explorer; Monitoring → Dashboards / Uptime checks.
  • CLI:
    gcloud run services logs read <service-name> --project "$PROJECT" --region "$REGION" --limit 50

3. Plane Application Behaviour​

  • First-deploy database setup. A db-init Job (postgres:15-alpine) idempotently creates the Plane database and user, grants privileges (including GRANT <user> TO postgres so ownership can be set), and shuts down its Cloud SQL Proxy sidecar cleanly. It runs on every apply and is safe to re-run.
  • Migrations run on startup, not in an init job. The AIO image's supervisord runs a migrator program (manage.py migrate) before the api and frontends come up. The startup probe allows up to ~5 minutes (30 s initial delay + 30 × 10 s failures) for a cold first boot.
  • Connection URLs are composed by the entrypoint. The platform entrypoint builds DATABASE_URL (private-IP TCP, sslmode=require; loopback with sslmode=disable on GKE), REDIS_URL (resolving $(NFS_SERVER_IP)), and AMQP_URL (from the sidecar's injected host), then execs Plane's bundled /app/start.sh. Look for the Composed DATABASE_URL / REDIS_URL / AMQP_URL log lines when debugging.
  • RabbitMQ is mandatory. Plane's start.sh validates AMQP_URL and exits if it is empty. The sidecar's broker state is ephemeral — queued Celery tasks are lost on instance recycle (documented hardening TODO).
  • First-run setup — God Mode. Open <web_url>/god-mode/ to create the instance admin and configure the instance (the entrypoint patches the internal Caddyfile with a 308 redirect from /god-mode to /god-mode/, working around a Remix SPA basename issue). Then sign up at the root URL and create your first workspace.
  • File uploads fail until S3 storage is wired. Everything else (issues, projects, cycles, modules) works; attachments and avatars need real S3 credentials (see §2D).
  • Background-work trade-off. Under the default cpu_always_allocated = false + min = 1, the instance stays alive but its CPU is throttled between requests, so background Celery work (notifications, webhooks, exports) may lag. For teams relying on timely notifications, set cpu_always_allocated = true; never lower min_instance_count to 0.
  • Health path. Startup and liveness probes and the uptime check target /health on port 80 (the internal Caddy proxy).
  • Verification:
    curl -s -o /dev/null -w "%{http_code}\n" "$SERVICE_URL/health"
    gcloud run services logs read <service-name> --region "$REGION" --limit 100 | grep -E "Composed|Starting Plane"

4. Configuration Variables​

Variables are grouped exactly as they appear on the deployment platform. Only settings specific to or notable for Plane are listed; every other input is inherited from App_CloudRun with its standard behaviour.

Group 1 — Project & Identity​

VariableDefaultDescription
project_id(required)Target Google Cloud project.
regionus-central1Region for the service and regional resources.

All other inputs follow standard App_CloudRun behaviour.

Group 2 — Deployment Environment​

VariableDefaultDescription
tenant_iddemoShort suffix that makes resource names unique per environment.
support_users[]Emails granted project access and monitoring alerts.

All other inputs follow standard App_CloudRun behaviour.

Group 3 — Application Identity​

VariableDefaultDescription
application_nameplaneBase name for resources. Do not change after first deploy.
display_namePlane - Project ManagementFriendly name shown in the Console.
application_versionstableAIO image tag. The upstream image has no latest tag — latest is mapped to stable at build time.

All other inputs follow standard App_CloudRun behaviour.

Group 4 — Runtime & Scaling​

VariableDefaultDescription
cpu_limit2000mThe AIO image runs many processes (api, workers, frontends, Caddy); 2 vCPU minimum.
memory_limit4Gi4 GiB recommended — raise if the migrator or worker OOMs.
cpu_always_allocatedfalseRequest-based billing: CPU is throttled between requests, so Celery notifications, webhooks, and exports may lag; set true for continuous background processing.
min_instance_count1Keep 1: the instance hosts Celery worker and beat, so at 0 periodic tasks stop once it is reaped.
max_instance_count3Cost ceiling.
container_port80The internal Caddy proxy port — the only port the AIO container exposes.
enable_cloudsql_volumetrueMounts the Cloud SQL socket volume (the entrypoint still connects over private-IP TCP).
execution_environmentgen2Required for NFS mounts.
timeout_seconds300Request timeout.

All other inputs follow standard App_CloudRun behaviour.

Group 5 — Access & Ingress Control​

VariableDefaultDescription
ingress_settingsallPublic internet access (required for browser access to Plane).
vpc_egress_settingPRIVATE_RANGES_ONLYPrivate-range egress via the VPC connector (Cloud SQL, Redis, NFS).

All other inputs follow standard App_CloudRun behaviour.

Group 6 — Environment Variables & Secrets​

VariableDefaultDescription
environment_variables{}Override or extend Plane's environment. Use this to supply real S3 credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_S3_BUCKET_NAME, AWS_S3_ENDPOINT_URL) once object storage is wired.
secret_environment_variables{}Map of env var → Secret Manager secret name.

All other inputs follow standard App_CloudRun behaviour.

Groups 7–10 — Backup, CI/CD, Custom SQL, Domain & CDN​

Standard App_CloudRun behaviour — see App_CloudRun. Notable for Plane: if you set application_domains, the custom domain becomes the host users reach, and WEB_URL / CORS_ALLOWED_ORIGINS / DOMAIN_NAME must resolve to it (override via environment_variables) or sign-in and email links break.

Group 11 — Storage & Filesystem​

VariableDefaultDescription
enable_nfstrueRequired for the Redis co-location on the NFS server VM (gen2).
create_cloud_storagetrueProvisions the storage bucket declared by Plane_Common.

All other inputs follow standard App_CloudRun behaviour.

Group 12 — Database Backend​

VariableDefaultDescription
database_typePOSTGRES_15Plane requires PostgreSQL — do not change to MySQL.
db_nameplane_dbDatabase name. Immutable after first deploy.
db_userplane_userApplication user. Immutable after first deploy.

All other inputs follow standard App_CloudRun behaviour.

Group 13 — Jobs & Scheduled Tasks​

VariableDefaultDescription
initialization_jobs[]Leave empty to use the built-in db-init job (postgres:15-alpine). Schema migrations are handled by the AIO image's own migrator on startup.

All other inputs follow standard App_CloudRun behaviour.

Group 14 — Observability & Health​

VariableDefaultDescription
startup_probeHTTP /health, 30 s initial delay, 30 failures × 10 sGenerous window (~5 min) for the first-boot migrator step.
liveness_probeHTTP /health, 30 s delay, period 30 sLiveness against the Caddy-fronted health endpoint.
uptime_check_configdisabled, path /healthCloud Monitoring uptime check; disabled by default.

All other inputs follow standard App_CloudRun behaviour.

Group 21 — Redis Cache​

VariableDefaultDescription
enable_redistrueRequired — Plane's Celery task queue and cache depend on Redis.
redis_host""Leave empty to use the NFS-VM-hosted Redis.
redis_port6379Redis port.

All other inputs follow standard App_CloudRun behaviour.

Group 22 — VPC Service Controls & Audit Logging​

Standard App_CloudRun behaviour (enable_vpc_sc, vpc_cidr_ranges, vpc_sc_dry_run, enable_audit_logging) — see App_CloudRun.


5. Outputs​

Returned on a successful deployment — the quickest way to locate and explore the running resources.

OutputDescription
service_nameCloud Run service name.
web_urlURL of the Plane web UI (the internal Caddy proxy serves web/space/admin/api on this single URL).
api_urlURL of the Plane API (same service URL, routed by the internal proxy).
service_locationRegion the service runs in.
stage_servicesStage-specific service details (Cloud Deploy).
load_balancer_ip / load_balancer_urlExternal HTTPS load balancer IP / URL (when enabled).
database_instance_nameCloud SQL instance name.
database_name / database_userApplication database name / user.
database_password_secretSecret Manager secret holding the DB password.
database_host / database_portDB endpoint (sensitive) / port.
storage_bucketsCreated Cloud Storage buckets (includes the storage bucket).
network_name / network_exists / regionsVPC network, presence, regions.
container_image / container_registryDeployed image and Artifact Registry repo.
monitoring_enabled / monitoring_notification_channels / uptime_check_namesMonitoring status, channels, uptime checks.
initialization_jobsNames of the setup jobs.
deployment_id / tenant_id / resource_prefixNaming identifiers.
project_id / project_numberProject identifiers.
cicd_enabled / github_repository_url / cicd_configurationCI/CD status and details.
artifact_registry_repository / cloudbuild_trigger_name / cloudbuild_trigger_idRegistry and build trigger.
vpc_sc_enabled / vpc_sc_perimeter_name / vpc_sc_dry_run_modeVPC-SC status.
audit_logging_enabled / artifact_registry_cmek_enabledAudit logging and CMEK status.

6. Configuration Pitfalls & Sensible Defaults​

Plan-time validations catch several of these; the rest surface only at runtime.

Risk: Critical (data loss / outage / security) — High (service degraded) — Medium (cost or partial degradation) — Low (minor).

SettingSensible valueRiskConsequence if wrong
database_typePOSTGRES_15CriticalPlane is a Django/PostgreSQL app; MySQL or NONE breaks the migrator and startup.
db_name / db_userset onceCriticalImmutable after first deploy; renaming recreates the DB/user and destroys all data.
RabbitMQ sidecarleave wiredCriticalPlane's start.sh exits if AMQP_URL is empty — the broker is mandatory, and it must be in-pod (Cloud Run cannot route AMQP between services).
container_port80CriticalThe AIO container only exposes the internal Caddy proxy on :80; any other port fails all probes.
application_versionstable or a real tagHighThe upstream image has no latest tag; the module maps latest→stable, but an invalid explicit tag 404s the build (MANIFEST_UNKNOWN).
enable_redistrueHighWithout Redis, Celery and caching have no backend; workers fail to start.
enable_nfstrue (when redis_host empty)HighThe default Redis lives on the NFS VM; disabling NFS with no external redis_host leaves Plane without a Redis endpoint.
startup_probe failure window≥ 30 × 10 sHighThe first-boot migrator can take minutes; a tight probe kills the instance mid-migration.
cpu_always_allocated / min_instance_counttrue / 1 for notification-critical teamsMediumUnder the default (false / 1), the instance's CPU is throttled between requests, so Celery notifications/webhooks/exports may lag.
Object storage (AWS_*)real S3 credentials before relying on uploadsMediumFile uploads (attachments, avatars) fail until HMAC keys or an external S3 endpoint are supplied — the rest of Plane works.
application_domains + URL env varskeep in syncMediumA custom domain that doesn't match WEB_URL/CORS_ALLOWED_ORIGINS/DOMAIN_NAME breaks sign-in redirects and email links.
memory_limit4GiMediumThe AIO image runs many processes; undersizing OOMs the migrator or Celery worker.
RabbitMQ durabilityaccept ephemeral or externalizeLowSidecar broker state is ephemeral; queued tasks are lost on instance recycle.

For the foundation behaviour referenced throughout — service identity, scaling and concurrency, ingress and load balancing, CI/CD, Cloud Armor, IAP, Binary Authorization, VPC-SC, backups, and image mirroring — see App_CloudRun. Plane-specific application configuration shared with the GKE variant is described in Plane_Common.

Need RAD to do something it does not do yet? Request it on the roadmap, or vote on what is already there.