Plane project management
Plane project management
This pattern composes Compute, Network, and Block Storage into a self-hosted project and work-management platform you run on infrastructure you control.
What this template does#
Provisions a single instance running Plane, an open-source project-management platform (a self-hosted alternative to Linear or Jira). Your team tracks issues, cycles, and projects on infrastructure you own:
- Compute instance that runs Plane's app, worker, and proxy containers in Docker alongside bundled PostgreSQL, Redis, RabbitMQ, and MinIO (8 vCPU and 8 GiB RAM)
- Private network, subnet, router, port, and security group; a floating IP for public access
- A block volume mounted at
/var/lib/docker, so the database, queue, and file-upload data live on a volume you can grow rather than on the boot disk - cloud-init installs Docker Engine, brings up PostgreSQL, Redis, RabbitMQ, and MinIO, and prepares Plane's app containers to start once you finish configuration
Plane's SECRET_KEY, the PostgreSQL password, the RabbitMQ password, and the MinIO root credentials are generated on first boot and written to /opt/plane/.env; no credential ships with this template.
A heavier stack than the other ops tools in this library#
Plane bundles four datastores (PostgreSQL, Redis, RabbitMQ, and MinIO for file uploads) plus its own six app containers (web, space, admin, api, worker, beat-worker) and a realtime service (live), fronted by Plane's own proxy container. This is a heavier footprint than the other self-hosted ops tools already in this library (Infisical, Outline, Uptime Kuma). Plane's own documentation recommends 4 vCPU and 8 GiB RAM at minimum; the default s1a.large flavor here provides 8 vCPU and 8 GiB RAM.
Parameters#
| Parameter | Description | Default |
|---|---|---|
key_name | SSH keypair name (must already exist) | No default |
flavor_name | Instance size (Plane's bundled datastores plus its app and proxy containers run on 8 vCPU / 8 GiB) | s1a.large |
image_name | Operating system image | Ubuntu-24.04 |
app_name | Display name prefix for resources | plane |
volume_size | Block volume size in GiB, mounted at /var/lib/docker | 40 |
external_network | External network for floating IP allocation | PublicStatic |
private_cidr | CIDR for the private subnet | 10.51.0.0/24 |
app_allowed_cidr | CIDR allowed to reach Plane's proxy on port 8080 | 10.51.0.0/24 |
Finish setup after apply#
cloud-init starts PostgreSQL, Redis, RabbitMQ, and MinIO, and writes the generated secrets to /opt/plane/.env. Complete the setup over SSH:
- Point a domain's DNS A record at the floating IP and put a reverse proxy (Caddy or Nginx) in front for HTTPS on 443.
- Edit
/opt/plane/.env: setWEB_URLandCORS_ALLOWED_ORIGINSto your public HTTPS address. - Run the one-shot database migration, create the MinIO uploads bucket, then start Plane:
cd /opt/plane
docker compose run --rm migrator
docker compose exec plane-minio mc alias set local http://plane-minio:9000 plane YOUR_MINIO_PASSWORD
docker compose exec plane-minio mc mb local/uploads
docker compose up -dAccess and security#
Plane's proxy container listens on port 8080 over plain HTTP, remapped from its default 80/443 so a host-level reverse proxy can own those ports for the public domain. The security group restricts 8080 to app_allowed_cidr, which defaults to the private network only. Because Plane needs a public URL for auth callbacks and workspace links, the normal access path is a domain with HTTPS on 443 behind a reverse proxy. Ports 80 and 443 stay open for that proxy; they carry no traffic until you add one.
When to use this pattern#
Track issues, cycles, modules, and views for a team on a host you operate. The bundled PostgreSQL, Redis, RabbitMQ, and MinIO suit a single-team deployment; to run PostgreSQL separately, point Plane's PGHOST/POSTGRES_* variables at a self-managed PostgreSQL instance and remove the bundled plane-db service from the compose file.
Estimated cost#
Monthly cost estimate
Pricing calculator ↗Sized as a custom package on shared vCPU.
Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.
What each resource is for
Plane project-management host
s1a.large · 8 shared vCPU, 8 GiB RAM, 0.5 Gbps
Runs Plane's app, worker, and proxy containers alongside its bundled PostgreSQL, Redis, RabbitMQ, and MinIO, with all datastore and upload data on an attached volume.
Plane's bundled datastores plus its app and proxy containers run on 8 vCPU and 8 GiB RAM. Size up for many concurrent users or large attachment volume.
Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.
Included in baseline
s1a.large
8 shared vCPU, 8 GiB RAM, 0.5 Gbps
Compute + RAM rate basis
8 vCPU + 8 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.
Block storage (70 GiB)
70 GiB at $0.08/GiB/mo
Public IP (included)
1 included with the custom package
Package promotional discount
Flat −$5.00/mo on the custom package (same promotion as named plans).
Included at no charge
These line items are zero on Quake AI. Many other providers meter them separately.
Data transfer (inbound and outbound)
Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.
AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.
Learn morePrivate networking
Private networks, subnets, Neutron routers, and security groups are included with the plan.
VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.
Control-plane API requests
OpenStack API calls for provisioning and management are included.
Some managed services on other clouds meter API calls or charge for premium control-plane features.
Pricing data last validated: . For current rates, check quake.ai/pricing.
Template source#
Show source (7 files)Hide source
data "openstack_images_image_v2" "os" {
name = var.image_name
most_recent = true
}
data "openstack_networking_network_v2" "external" {
name = var.external_network
}
resource "openstack_networking_network_v2" "private" {
name = "${var.app_name}-net"
admin_state_up = true
}
resource "openstack_networking_subnet_v2" "private" {
name = "${var.app_name}-subnet"
network_id = openstack_networking_network_v2.private.id
cidr = var.private_cidr
ip_version = 4
dns_nameservers = ["1.1.1.1", "8.8.8.8"]
}
resource "openstack_networking_router_v2" "main" {
name = "${var.app_name}-router"
external_network_id = data.openstack_networking_network_v2.external.id
}
resource "openstack_networking_router_interface_v2" "private" {
router_id = openstack_networking_router_v2.main.id
subnet_id = openstack_networking_subnet_v2.private.id
}
resource "openstack_networking_secgroup_v2" "plane" {
name = "${var.app_name}-sg"
description = "SSH and HTTP/HTTPS for a reverse proxy; app port 8080 restricted"
}
resource "openstack_networking_secgroup_rule_v2" "ssh" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 22
port_range_max = 22
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.plane.id
}
# 80 and 443 carry Plane when it is served over a domain with automatic TLS
# through a reverse proxy (Caddy or Nginx). Plane needs a stable public URL
# for auth callbacks and workspace links, so the domain path is the expected
# way to reach it; see the reference page.
resource "openstack_networking_secgroup_rule_v2" "http" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 80
port_range_max = 80
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.plane.id
}
resource "openstack_networking_secgroup_rule_v2" "https" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 443
port_range_max = 443
remote_ip_prefix = "0.0.0.0/0"
security_group_id = openstack_networking_secgroup_v2.plane.id
}
# Raw proxy HTTP on 8080 (Plane's own proxy container, remapped off 80/443 so
# the host-level reverse proxy can own those) is restricted to
# app_allowed_cidr. Use it for setup over an SSH tunnel or a scoped
# workstation IP; put a reverse proxy on 443 in front for routine access.
resource "openstack_networking_secgroup_rule_v2" "app" {
direction = "ingress"
ethertype = "IPv4"
protocol = "tcp"
port_range_min = 8080
port_range_max = 8080
remote_ip_prefix = var.app_allowed_cidr
security_group_id = openstack_networking_secgroup_v2.plane.id
}
resource "openstack_networking_port_v2" "plane" {
name = "${var.app_name}-port"
network_id = openstack_networking_network_v2.private.id
security_group_ids = [openstack_networking_secgroup_v2.plane.id]
fixed_ip {
subnet_id = openstack_networking_subnet_v2.private.id
}
depends_on = [openstack_networking_router_interface_v2.private]
}
resource "openstack_blockstorage_volume_v3" "data" {
name = "${var.app_name}-data"
size = var.volume_size
}
resource "openstack_compute_instance_v2" "plane" {
name = var.app_name
flavor_name = var.flavor_name
key_pair = var.key_name
user_data = templatefile("${path.module}/cloud-init/plane.yaml.tftpl", {
app_name = var.app_name
})
block_device {
uuid = data.openstack_images_image_v2.os.id
source_type = "image"
destination_type = "volume"
volume_size = 30
boot_index = 0
delete_on_termination = true
}
network {
port = openstack_networking_port_v2.plane.id
}
}
resource "openstack_compute_volume_attach_v2" "data" {
instance_id = openstack_compute_instance_v2.plane.id
volume_id = openstack_blockstorage_volume_v3.data.id
}
resource "openstack_networking_floatingip_v2" "plane" {
pool = var.external_network
}
resource "openstack_networking_floatingip_associate_v2" "plane" {
floating_ip = openstack_networking_floatingip_v2.plane.address
port_id = openstack_networking_port_v2.plane.id
}
variable "key_name" {
description = "SSH keypair name (must already exist in your project)"
type = string
}
variable "flavor_name" {
description = "Instance size. Plane bundles four datastores (PostgreSQL, Redis, RabbitMQ, MinIO) plus its own app and proxy containers on this one host, so it needs more headroom than the other self-hosted ops tools in this library. The default s1a.large (8 vCPU / 8 GiB) exceeds Plane's own documented recommended spec (4 vCPU / 8 GiB). Size up for many concurrent users or large attachment volume."
type = string
default = "s1a.large"
}
variable "image_name" {
description = "Operating system image. Ubuntu 24.04 is the recommended base."
type = string
default = "Ubuntu-24.04"
}
variable "app_name" {
description = "Display name prefix for compute and network resources"
type = string
default = "plane"
}
variable "volume_size" {
description = "Block volume size in GiB, mounted at /var/lib/docker. Plane's PostgreSQL database, RabbitMQ queue data, and MinIO file uploads all live on this volume rather than the boot disk, so it needs more room than the other ops-tools templates."
type = number
default = 40
}
variable "external_network" {
description = "Shared external network for router gateway and floating IPs; defaults to PublicStatic (persisted FIP / production pattern). Override with PublicEphemeral for ephemeral demos."
type = string
default = "PublicStatic"
}
variable "private_cidr" {
description = "CIDR for the private tenant network the instance lives in"
type = string
default = "10.51.0.0/24"
}
variable "app_allowed_cidr" {
description = "CIDR allowed to reach Plane's proxy container on port 8080. Defaults to the private network only, so the app is not exposed to the public internet on its raw port. Plane needs a stable public URL for auth callbacks and workspace links, so serve it over a domain with HTTPS on 443 behind a reverse proxy. To reach port 8080 directly from your workstation during setup, set this to YOUR_IP/32."
type = string
default = "10.51.0.0/24"
}
output "instance_id" {
description = "ID of the compute instance running Plane"
value = openstack_compute_instance_v2.plane.id
}
output "floating_ip" {
description = "Public floating IP address of the Plane host"
value = openstack_networking_floatingip_v2.plane.address
}
output "private_ip" {
description = "Private IP address of the instance"
value = openstack_compute_instance_v2.plane.access_ip_v4
}
output "app_url" {
description = "Plane proxy URL on port 8080. Reachable from app_allowed_cidr (the private network by default). Plane needs a stable public URL for auth callbacks and workspace links; put a reverse proxy in front and use HTTPS on 443, then set WEB_URL and CORS_ALLOWED_ORIGINS in /opt/plane/.env."
value = "http://${openstack_networking_floatingip_v2.plane.address}:8080"
}
terraform {
required_version = ">= 1.6.0"
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = "~> 2.0"
}
}
}
provider "openstack" {}
# Required: SSH keypair must already exist in your project
key_name = "YOUR_KEY_NAME"
# Recommended: restrict the app port (8080) to your workstation IP for setup.
# Leave unset to keep 8080 reachable only from the private network and tunnel
# over SSH. Plane needs a stable public URL for auth callbacks and workspace
# links, so the normal access path is a domain with HTTPS on 443 behind a
# reverse proxy.
# app_allowed_cidr = "203.0.113.10/32"
# flavor_name = "s1a.large"
# image_name = "Ubuntu-24.04"
# app_name = "plane"
# volume_size = 40
# external_network = "PublicStatic"
# private_cidr = "10.51.0.0/24"
#cloud-config
package_update: true
packages:
- ca-certificates
- curl
write_files:
- path: /opt/plane/docker-compose.yml
permissions: "0644"
content: |
# Plane project management for ${app_name}. Plane's own proxy container
# listens on 8080 (remapped from its default 80/443 so a host-level
# reverse proxy can own those ports). Plane needs a stable public URL
# (WEB_URL, CORS_ALLOWED_ORIGINS) before auth callbacks and workspace
# links work: set them in /opt/plane/.env, run the one-shot migrator,
# then start the app and proxy services. No credential ships with this
# template: SECRET_KEY, the PostgreSQL password, the RabbitMQ password,
# and the MinIO root credentials are generated on first boot. cloud-init
# starts only the four infra containers (PostgreSQL, Redis, RabbitMQ,
# MinIO); bring up the rest after you finish configuring
# /opt/plane/.env.
services:
web:
image: makeplane/plane-frontend:latest
restart: unless-stopped
env_file:
- /opt/plane/.env
depends_on:
- api
space:
image: makeplane/plane-space:latest
restart: unless-stopped
env_file:
- /opt/plane/.env
depends_on:
- api
- web
admin:
image: makeplane/plane-admin:latest
restart: unless-stopped
env_file:
- /opt/plane/.env
depends_on:
- api
- web
api:
image: makeplane/plane-backend:latest
restart: unless-stopped
command: ./bin/docker-entrypoint-api.sh
env_file:
- /opt/plane/.env
depends_on:
- plane-db
- plane-redis
- plane-mq
worker:
image: makeplane/plane-backend:latest
restart: unless-stopped
command: ./bin/docker-entrypoint-worker.sh
env_file:
- /opt/plane/.env
depends_on:
- api
- plane-db
- plane-redis
beat-worker:
image: makeplane/plane-backend:latest
restart: unless-stopped
command: ./bin/docker-entrypoint-beat.sh
env_file:
- /opt/plane/.env
depends_on:
- api
- plane-db
- plane-redis
migrator:
image: makeplane/plane-backend:latest
restart: "no"
command: ./bin/docker-entrypoint-migrator.sh
env_file:
- /opt/plane/.env
depends_on:
- plane-db
- plane-redis
live:
image: makeplane/plane-live:latest
restart: unless-stopped
env_file:
- /opt/plane/.env
plane-db:
image: postgres:15.7-alpine
restart: unless-stopped
command: postgres -c 'max_connections=1000'
env_file:
- /opt/plane/.env
volumes:
- plane_pg:/var/lib/postgresql/data
plane-redis:
image: valkey/valkey:7.2.11-alpine
restart: unless-stopped
volumes:
- plane_redis:/data
plane-mq:
image: rabbitmq:3.13.6-management-alpine
restart: unless-stopped
env_file:
- /opt/plane/.env
volumes:
- plane_rabbitmq:/var/lib/rabbitmq
plane-minio:
image: minio/minio:latest
restart: unless-stopped
command: server /export --console-address ":9090"
env_file:
- /opt/plane/.env
volumes:
- plane_uploads:/export
proxy:
image: makeplane/plane-proxy:latest
restart: unless-stopped
env_file:
- /opt/plane/.env
ports:
- "8080:80"
- "8443:443"
depends_on:
- web
- api
- space
- admin
- live
volumes:
plane_pg:
plane_redis:
plane_rabbitmq:
plane_uploads:
runcmd:
- |
set -e
# The data volume attaches as /dev/sdb on this platform (not /dev/vdb).
# Mount it at /var/lib/docker before Docker is installed so the
# PostgreSQL database, the RabbitMQ queue data, and MinIO's uploads live
# on the resizable volume rather than the boot disk.
DEV=/dev/sdb
for i in $(seq 1 30); do [ -b "$DEV" ] && break; sleep 5; done
if ! blkid "$DEV" >/dev/null 2>&1; then mkfs.ext4 -F -L planedata "$DEV"; fi
mkdir -p /var/lib/docker
mount "$DEV" /var/lib/docker
grep -q "$DEV" /etc/fstab || echo "$DEV /var/lib/docker ext4 defaults,nofail 0 2" >> /etc/fstab
# Install Docker Engine plus the compose plugin from Docker's convenience
# script.
curl -fsSL https://get.docker.com | sh
# Generate Plane's Django secret key, the bundled PostgreSQL password,
# the RabbitMQ password, and the MinIO root credentials on first boot.
# These never leave this instance.
SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')
PGPASS=$(openssl rand -hex 24)
RABBITPASS=$(openssl rand -hex 24)
MINIOPASS=$(openssl rand -hex 24)
umask 077
{
echo "SECRET_KEY=$SECRET_KEY"
echo "POSTGRES_USER=plane"
echo "POSTGRES_PASSWORD=$PGPASS"
echo "POSTGRES_DB=plane"
echo "PGDATA=/var/lib/postgresql/data"
echo "PGHOST=plane-db"
echo "POSTGRES_PORT=5432"
echo "REDIS_HOST=plane-redis"
echo "REDIS_PORT=6379"
echo "RABBITMQ_HOST=plane-mq"
echo "RABBITMQ_PORT=5672"
echo "RABBITMQ_USER=plane"
echo "RABBITMQ_PASSWORD=$RABBITPASS"
echo "RABBITMQ_VHOST=plane"
echo "AWS_REGION="
echo "AWS_ACCESS_KEY_ID=plane"
echo "AWS_SECRET_ACCESS_KEY=$MINIOPASS"
echo "AWS_S3_ENDPOINT_URL=http://plane-minio:9000"
echo "AWS_S3_BUCKET_NAME=uploads"
echo "FILE_SIZE_LIMIT=5242880"
echo "USE_MINIO=1"
echo "SITE_ADDRESS=:80"
echo "# Set WEB_URL and CORS_ALLOWED_ORIGINS to your public HTTPS"
echo "# address before running the migrator or starting Plane:"
echo "# WEB_URL=https://plane.example.com"
echo "# CORS_ALLOWED_ORIGINS=https://plane.example.com"
} > /opt/plane/.env
chmod 600 /opt/plane/.env
# Bring up the infra layer only. Plane's app, worker, migrator, and
# proxy containers start after you set WEB_URL and CORS_ALLOWED_ORIGINS
# in /opt/plane/.env and run:
# cd /opt/plane && docker compose run --rm migrator
# cd /opt/plane && docker compose up -d
cd /opt/plane
docker compose up -d plane-db plane-redis plane-mq plane-minio
# Plane project management
Single compute instance running [Plane](https://plane.so), a self-hosted project and work-management platform (a self-hosted alternative to Linear or Jira) on infrastructure you control. After apply, you set a public URL, run a one-shot database migration, start the app, sign up the first admin account, and create a workspace, a project, and your first issue.
**Network class:** production — `external_network` defaults to `PublicStatic` for persisted floating IPs and multi-tier stacks; override with `PublicEphemeral` for ephemeral demos.
The instance provisions a private network, a floating IP, and a block volume mounted at `/var/lib/docker` so the datastore and upload data lives on a resizable volume. cloud-init installs Docker Engine, brings up the bundled PostgreSQL, Redis, RabbitMQ, and MinIO, and prepares Plane's app, worker, and proxy containers to start once you finish configuration.
## Where this fits
Plane centralizes issues, cycles (sprints), modules, and views for a team, on infrastructure you own rather than on a third-party SaaS.
## A heavier stack than the other ops tools in this library
Plane bundles four datastores (PostgreSQL, Redis, RabbitMQ, and MinIO for file uploads) plus its own six app containers (web, space, admin, api, worker, beat-worker) and a realtime service (live), fronted by Plane's own proxy container. This is a heavier footprint than the other self-hosted ops tools in this library (Infisical, Outline, Uptime Kuma). Plane's own documentation recommends 4 vCPU and 8 GiB RAM at minimum; the default `s1a.large` flavor here provides 8 vCPU and 8 GiB RAM, well above that floor.
## Prerequisites
- OpenTofu >= 1.6.0 or Terraform >= 1.6.0
- Quake AI account with OpenStack credentials
- An existing SSH keypair in your project (the value of `key_name` must match that keypair)
- A domain you can point at the instance (Plane needs a stable public URL for auth callbacks and workspace links)
## Resource baseline
Plane's bundled datastores plus its app and proxy containers run on 8 vCPU and 8 GiB RAM. The default `s1a.large` flavor exceeds Plane's own documented recommended spec (4 vCPU / 8 GiB). Size up for many concurrent users or large attachment volume.
## Usage
1. Clone or copy this template directory
2. Copy `terraform.tfvars.example` to `terraform.tfvars` and fill in your values
3. Source your OpenStack credentials: `source openrc.sh`
4. Initialize: `tofu init`
5. Preview: `tofu plan`
6. Apply: `tofu apply`
After apply, cloud-init installs Docker, generates Plane's `SECRET_KEY`, the PostgreSQL password, the RabbitMQ password, and the MinIO root credentials into `/opt/plane/.env`, and starts PostgreSQL, Redis, RabbitMQ, and MinIO. Finish the configuration over SSH:
1. Point a domain's DNS A record at `floating_ip` and put a reverse proxy (Caddy or Nginx) in front for HTTPS on 443.
2. Edit `/opt/plane/.env`: set `WEB_URL` and `CORS_ALLOWED_ORIGINS` to your public HTTPS address.
3. Run the one-shot database migration, create the MinIO uploads bucket, then start Plane:
```bash
cd /opt/plane
docker compose run --rm migrator
docker compose exec plane-minio mc alias set local http://plane-minio:9000 plane YOUR_MINIO_PASSWORD
docker compose exec plane-minio mc mb local/uploads
docker compose up -d
```
No credential ships with this template: `SECRET_KEY`, the PostgreSQL password, the RabbitMQ password, and the MinIO root credentials are generated on first boot.
## Access and security
Plane's proxy container listens on port 8080 over plain HTTP (remapped from its default 80/443 so a host-level reverse proxy can own those ports for the public domain). The security group restricts 8080 to `app_allowed_cidr`, which defaults to the private network only. Because Plane needs a public URL for auth callbacks and workspace links, the normal access path is a domain with HTTPS on 443 behind a reverse proxy. Point the domain's DNS A record at `floating_ip`. Ports 80 and 443 stay open for that reverse proxy; they carry no traffic until you add one.
## Datastores
This template bundles PostgreSQL, Redis, RabbitMQ, and MinIO as containers on the same instance, which suits a single-team Plane instance. To run PostgreSQL as a separate service, point Plane's `PGHOST`/`POSTGRES_*` variables in `/opt/plane/.env` at a [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres) instance and remove the bundled `plane-db` service from the compose file.
## Variables
| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `key_name` | string | yes | n/a | SSH keypair name (must already exist in your project) |
| `flavor_name` | string | no | `s1a.large` | Instance size (Plane's bundled datastores plus its app and proxy containers run on 8 vCPU / 8 GiB) |
| `image_name` | string | no | `Ubuntu-24.04` | Operating system image |
| `app_name` | string | no | `plane` | Display name prefix for resources |
| `volume_size` | number | no | `40` | Block volume size in GiB, mounted at `/var/lib/docker` |
| `external_network` | string | no | `PublicStatic` | Persisted FIP / production default; override with `PublicEphemeral` for demos |
| `private_cidr` | string | no | `10.51.0.0/24` | CIDR for the private subnet |
| `app_allowed_cidr` | string | no | `10.51.0.0/24` | CIDR allowed to reach Plane's proxy on port 8080 |
## Outputs
| Name | Description |
| --- | --- |
| `floating_ip` | Public floating IP assigned to the instance |
| `private_ip` | Private IP address of the instance |
| `app_url` | Plane proxy URL on port 8080 |
| `instance_id` | Compute instance ID |
## Scope
This is a single-VM Plane host that you operate, not a managed multi-tenant project-management service. It is CPU-only and runs in one region, and it bundles four datastores as containers on the same host. You operate the instance, Docker, Plane's containers, PostgreSQL, Redis, RabbitMQ, MinIO, and the data volume yourself: back them up, patch them, and watch resource use as the team grows. For a larger team, move PostgreSQL and the other datastores onto their own instances and size the app host up.
## Documentation
See also: [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres), [Redis](/resources/iac-templates/redis-cache)
Resources, parameters, and variables
key_namerequiredflavor_name="s1a.large"image_name="Ubuntu-24.04"app_name="plane"volume_size=40external_network="PublicStatic"private_cidr="10.51.0.0/24"app_allowed_cidr="10.51.0.0/24"
Customize this pattern#
- Customize a template's image and flavor
- Add a block volume to a template
- Parameterize a template with a tfvars file
See also#
Usage Guidelines
The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.
For the full policy, see Usage Guidelines.
See Also
Terraform and OpenTofu on Quake AI
Prerequisite
Networks
Prerequisite
Authoring IaC templates for Quake AI
Shares: Volumes, Security Groups
Deploy an API gateway with the api-gateway template
Shares: Volumes, Security Groups
Deploy a regional edge cache with the edge-cache template
Shares: Volumes, Security Groups