Installation

This guide stands up a single-host Paas instance on a VM that has Docker installed.

Prerequisites

  • A Linux VM (Ubuntu 24.04 tested) with public IP and root/sudo access.
  • Docker Engine ≥ 24 with the Compose v2 plugin (docker compose version).
  • openssl (used by the installer to generate secrets).
  • Two DNS records pointing at the VM (you'll add them after install):
    • paas.example.com — the dashboard + API.
    • *.apps.example.com — wildcard for auto-assigned app hostnames.
    • git.example.com — git remote (can be the same A record as the dashboard).

If you're just trying it out, you can skip DNS and use paas.localhost, apps.localhost, git.localhost — see the Local-only quickstart below.

1. Clone

git clone <this-repo> paas
cd paas

2. First-run installer

./deploy/installer/install.sh

The installer prompts for four things (platform hostname, admin email, admin password, whether to use Let's Encrypt staging) and generates strong random values for everything else. It writes .env (mode 600), brings the stack up, and waits for the platform Let's Encrypt cert to issue.

Prerequisite: DNS A record for the platform hostname must already point at this server's public IP, with port 80 reachable from the internet so the HTTP-01 ACME challenge succeeds.

The installer creates the paas-secrets Docker volume containing /etc/paas/master.key (32 bytes, mode 600), pre-creates the paas-control and paas-apps networks, builds images, runs migrations, and starts the stack.

3. Sign in

Open https://paas.example.com/. Sign in with the bootstrap admin printed at the end of the installer (also in your .env as PAAS_BOOTSTRAP_EMAIL / PAAS_BOOTSTRAP_PASSWORD).

4. Add an SSH key

Account → SSH keys → paste your ~/.ssh/id_ed25519.pub. This is required before you can git push to your apps.

5. (Recommended) verify the deploy loop on staging certs

PAAS_ACME_STAGING=true is the default. Create an org and an app, push a commit, hit your custom domain. Once you see end-to-end works (with a browser warning about the staging cert), set PAAS_ACME_STAGING=false in .env and re-issue:

docker compose restart cert-manager

This avoids burning real Let's Encrypt rate limits while you're shaking out your DNS / config.

Local-only quickstart (no real DNS)

For a hack-on-it-locally setup, edit /etc/hosts:

127.0.0.1 paas.localhost git.localhost
127.0.0.1 my-acme-acme.apps.localhost

…and set in .env:

PAAS_PLATFORM_HOSTNAME=paas.localhost
PAAS_GIT_PUBLIC_HOST=git.localhost
PAAS_WILDCARD_BASE=apps.localhost
PAAS_HTTP_PORT=80

ACME will fail (Let's Encrypt won't validate localhost) — that's fine for local dev; HTTP-only access still works on http://paas.localhost.

Where things live

Path on host What it is
Volume paas-secrets /etc/paas/master.key, ACME account
Volume pg-data Control-plane Postgres
Volume repos Bare git repos for all apps
Volume registry-data Local Docker registry
Volume nginx-confd Generated vhost configs
Volume certs Issued TLS certs
./deploy/nginx/nginx.conf Public-facing nginx config (read-only)

Updating

git pull
docker compose -f docker-compose.yml build
docker compose -f docker-compose.yml run --rm migrate
docker compose -f docker-compose.yml up -d

Uninstall

docker compose -f docker-compose.yml down
docker volume rm pg-data registry-data repos paas-secrets nginx-confd certs \
  acme-account acme-challenges builder-work
docker network rm paas-control paas-apps