Pl
Applications Azure

Plane on Ubuntu 24.04 on Azure User Guide

| Product: Plane on Ubuntu 24.04 LTS on Azure

Overview

This image runs Plane v1.3.1, the open source project and issue tracking tool from makeplane, on Ubuntu 24.04 LTS. Plane is a self-hosted alternative to Jira and Linear: teams organise work into workspaces and projects, then track it as issues (work items) with states, priorities, labels, assignees and estimates, grouped into cycles and modules and viewed as lists, kanban boards, calendars or spreadsheets.

The image ships the stock upstream Plane Community self-host stack run with Docker Compose under a systemd service that starts it on boot. The stack is a bundled PostgreSQL 15 database, a Valkey cache, a RabbitMQ queue and a MinIO object store for attachments, together with the Plane API, background workers, the web, space and admin front ends and a websocket service, all behind a built-in reverse proxy on port 80. Every image is pinned by tag or digest, and the whole stack is baked into the image so nothing is pulled on first boot.

What is included:

  • Plane v1.3.1 (stock upstream makeplane/plane-* images, pinned), run by systemd via Docker Compose
  • The Plane web console, REST API and websocket service behind a reverse proxy on :80
  • A bundled PostgreSQL 15, Valkey, RabbitMQ and MinIO, all on the same VM
  • A per-VM database password, storage keys, application secret key and queue password generated on first boot
  • A first administrator user with a per-VM password, and a per-VM administrative REST API key, generated on first boot and recorded in a root-only file
  • A ready-to-use workspace and "Getting Started" project seeded on first boot
  • docker.service, plane-configure.service, plane-firstboot.service and plane.service as systemd units, enabled and active
  • No baked-in administrator account or shipped data; an 8 GiB swapfile so the stack runs comfortably on Standard_B2s
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point; size up for heavier teams. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web console (and 443/tcp if you terminate TLS). Plane serves plain HTTP on port 80; for production, front it with your own domain and TLS (see the production hardening step). The stack takes two to four minutes to finish its first-boot database migrations after the VM comes up.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Plane by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name plane \
  --image <marketplace-image-urn> \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard

az vm open-port --resource-group <your-rg> --name plane --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the stack is running

The systemd units bring the whole Docker Compose stack up on boot. Confirm the units are active and every container is running:

sudo systemctl is-active docker plane-configure plane-firstboot plane
sudo docker compose -f /opt/plane/docker-compose.yaml --env-file /opt/plane/plane.env ps --format '{{.Service}}\t{{.Status}}' | sort

You should see the units active and all twelve services (api, worker, beat-worker, web, space, admin, live, proxy, plane-db, plane-redis, plane-mq and plane-minio) up.

The Plane systemd units active and every container in the Docker Compose stack up and running

Step 5 - Retrieve your per-VM credentials

On first boot the appliance generates every secret uniquely for this VM and writes the ones you need to a root-only file. Read it with the secret values redacted:

sudo sed -E 's/^(PLANE_ADMIN_PASSWORD=).*/\1********/; s/^(PLANE_API_KEY=).*/\1********/' /root/plane-credentials.txt

The file holds your web console URL, the administrator email and password, the per-VM REST API key, and the seeded workspace slug and project id. Store the password and API key somewhere safe.

The per-VM Plane credentials file with the console URL, administrator email, and the password and API key redacted

Step 6 - Confirm the API and the REST API key

Confirm the Plane instance API and web app answer through the proxy on port 80:

curl -s http://127.0.0.1/api/instances/ | jq '{instance: .instance.instance_name, version: .instance.current_version, setup: .instance.is_setup_done}'
curl -s -o /dev/null -w "web app -> HTTP %{http_code}\n" http://127.0.0.1/

The Plane instance API returning the instance name and version, and the web app answering 200 on port 80

Now prove the per-VM REST API key end to end: a bogus key is rejected, the real key lists projects, and you can create a work item and read it back. The commands read the key, workspace slug and project id straight from the credentials file:

API_KEY=$(sudo grep '^PLANE_API_KEY=' /root/plane-credentials.txt | cut -d= -f2-)
SLUG=$(sudo grep '^PLANE_WORKSPACE_SLUG=' /root/plane-credentials.txt | cut -d= -f2-)
PROJECT=$(sudo grep '^PLANE_PROJECT_ID=' /root/plane-credentials.txt | cut -d= -f2-)
BASE="http://127.0.0.1/api/v1/workspaces/$SLUG"
curl -s -o /dev/null -w "bogus key  -> HTTP %{http_code} (rejected)\n" -H "X-API-Key: bogus" "$BASE/projects/"
curl -s -o /dev/null -w "per-VM key -> HTTP %{http_code}\n" -H "X-API-Key: $API_KEY" "$BASE/projects/"
ITEM=$(curl -s -X POST "$BASE/projects/$PROJECT/issues/" -H "X-API-Key: $API_KEY" -H 'Content-Type: application/json' -d '{"name":"Ship the release"}' | jq -r .id)
curl -s "$BASE/projects/$PROJECT/issues/$ITEM/" -H "X-API-Key: $API_KEY" | jq -r '"created + read back: " + .name'

The per-VM REST API key accepted, a bogus key rejected, and a work item created via the API and read back

Step 7 - Sign in to the web console

Open http://<vm-public-ip>/ in a browser. Sign in with the administrator email and password from your credentials file. There is no default account baked into the image; the credentials are unique to this VM.

The Plane sign-in page

Step 8 - Open your workspace and project

After signing in you land in the seeded cloudimg workspace. Its Projects view lists the "Getting Started" project that first boot created for you. Create more projects from here, or open the seeded one to start working.

The Plane workspace projects view showing the seeded Getting Started project

Step 9 - Work with issues on the board

Open a project's Work items to see its issues. Each work item carries a state, priority, assignees, labels and estimates, and you can switch between list, board (kanban), calendar and spreadsheet layouts, and group work into cycles and modules.

The Plane project work items list showing several seeded issues with their states

Step 10 - Open a work item

Click a work item to open its detail panel, where you set its state, assignees, priority, dates, labels and description, add sub-items, links and comments, and follow its full activity history.

A Plane work item detail panel showing its properties, state and creator

Step 11 - Production hardening

For a production deployment, put Plane behind your own domain name and TLS. Plane's built-in proxy can terminate TLS for you: set a real hostname and a certificate email in the environment file and restart the stack. The appliance re-derives its public address on every boot, so the console works over the VM's public IP out of the box, but you should point a DNS record at the VM and serve HTTPS before inviting your team. Edit /opt/plane/plane.env (set APP_DOMAIN, WEB_URL, CORS_ALLOWED_ORIGINS, SITE_ADDRESS and, for automatic certificates, CERT_EMAIL), then run sudo systemctl restart plane. Keep the credentials file readable only by root, and restrict the network security group to the smallest set of source addresses that need access.

Source code and licensing

Plane is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0). This image ships stock, unmodified upstream Plane Community Edition images configured only through the environment file and systemd. Under section 13 of the AGPL, the complete corresponding source for the version running on this appliance is the pinned upstream release, available at github.com/makeplane/plane (release v1.3.1). The cloudimg charge covers packaging, security patching, image maintenance and support, not the Plane software itself, which remains free.

Maintenance

The Ubuntu base ships fully patched with unattended security upgrades enabled. To update Plane itself, follow the upstream self-host upgrade process for the pinned release line. The bundled PostgreSQL data lives on the VM's OS disk under a Docker volume; back it up with your normal disk-snapshot or pg_dump workflow before upgrades.

Support

This image is maintained by cloudimg with 24/7 support. If you need help deploying or operating Plane on Azure, contact cloudimg support through the Azure Marketplace listing or at cloudimg.co.uk.