Skip to content

Deploy with Docker Compose

A site with the Docker Compose runtime is a Compose project that Falak deploys, routes, observes and rolls back. Use it for apps made of several containers (an app plus Redis, a worker, a search engine) or anything published as a compose.yaml. Templates are Compose sites too.

  • A server of type app, web or worker with Docker installed (tick Install Docker Engine when you create the server).
  • For a Compose file in your repository: a builder that can do Docker builds (a builder server), even if no service uses build:.
Source API value Compose file build: allowed
Repository repo compose_files in the repository (-f order, plus compose_profiles); default compose.yaml, then docker-compose.yml Yes (built by the builder, pushed to Falak’s registry, referenced by digest)
Inline inline Pasted into Falak, versioned (history and restore, newest 50 kept) No
  1. + Create → Git repository, then choose Docker Compose app as the app type and enter the compose file (or paste a Compose file, or deploy a template).
  2. Decide where each service runs: in the stack, public (with its container port, a domain and an optional health check path), as a Falak database, or as its own Falak site.
  3. Pick servers. Every server runs the whole project (replicated).
  4. Create. The first deploy starts.

The repository flow (override files, profiles, the services table, variables and Falak’s adjustments) is described in Compose apps from git.

Through the API, create the site with runtime: "compose":

POST /api/v1/sites (body)
{
"name": "Wiki",
"runtime": "compose",
"server_ids": ["01k…"],
"compose_source": "inline",
"compose_content": "services:\n app:\n image: ghcr.io/requarks/wiki:2.5\n expose: [\"3000\"]\n",
"public_services": [{"service": "app", "port": 3000, "domain": {"type": "generated"}}],
"variables": {"DB_PASS": "change-me"}
}

For every release Falak renders the file the servers receive:

  • Ports: host ports: mappings are removed from all services. Each public service gets 127.0.0.1:<allocated host port>:<container port>, and Caddy routes its domain there with HTTPS.
  • Images: built services are pinned to the built image digest; pulled images are pinned to the digest the server resolved on the first deploy, so a rollback is exact.
  • Variables: interpolation stays Compose-native (${VAR}). Your site variables (with ${{ service.KEY }} references resolved) are written to the project’s .env next to compose.yaml, plus FALAK_SITE_ID, FALAK_SERVER_ID, FALAK_DEPLOYMENT_ID and FALAK_RELEASE_ID.
  • Restart policy: in repository and inline stacks, every service without one gets restart: unless-stopped, so the stack comes back after a server reboot. Inline stacks (including ones created through the API) get it at every deploy since v0.7.1; before, they stayed Exited after a reboot.
  • Repository stacks also get Falak adjustments: container_name removed, missing bind sources as named volumes, repository files shipped with the release.
  • Labels: every service gets falak.site, falak.release and falak.service for log and metric attribution.
  • Project name: the site slug. Because it never changes, named volumes persist across releases.
Strategy: compose
FETCH write releases/<ID>/compose.yaml + .env, docker compose pull
LEADER docker compose run --rm <service> <command> for each falak.deploy.leader_command label
ACTIVATE docker compose up -d --remove-orphans --wait --wait-timeout 300
HEALTH HTTP check of every public service through the edge
ROLLBACK on failure: up --wait with the previous release's files

Health checks: each public service is requested through its own domains at its health check path; without one, the primary public service uses the site’s health path and expected status, and other public services must answer / with a status below 500. Give services Docker healthchecks so --wait means “ready”. Raise the wait with FALAK_COMPOSE_WAIT_TIMEOUT (seconds) for slow starters.

Add the label falak.deploy.leader_command to a service to run a command once per deployment, on the leader, before activation (migrations, for example):

compose.yaml
services:
app:
image: ghcr.io/acme/app:2.3.0
expose: ["8080"]
labels:
falak.deploy.leader_command: "php artisan migrate --force"

Falak runs docker compose run --rm app php artisan migrate --force. Arguments are split like a shell would, and quotes must be balanced.

Service Generated domain Test domain
First public service (primary) <service>-<slug>.<leader-ip-with-dashes>.sslip.io <slug>.<FALAK_TEST_DOMAIN>
Other public services <service>-<slug>.<ip>.sslip.io <service>-<slug>.<FALAK_TEST_DOMAIN>

Every public service has domains of its own: add several per service, with Cloudflare records, cache modes and routing rules, in Settings → Networking (service picker). See Edge for every public service.

Unless an admin enables Allow privileged compose (organization setting under Settings → Compose, permission sites.compose.policy), Falak refuses a Compose file — inline files when you save them, and every source (repository, inline, template) when a release is rendered — if a service uses:

  • privileged: true
  • network_mode: host or pid: host
  • cap_add beyond Docker’s default set (AUDIT_WRITE, CHOWN, DAC_OVERRIDE, FOWNER, FSETID, KILL, MKNOD, NET_BIND_SERVICE, NET_RAW, SETFCAP, SETGID, SETPCAP, SETUID, SYS_CHROOT)
  • host bind mounts outside the release directory
  • devices
  • a /var/run/docker.sock mount

Named volumes are always allowed. Compose files are limited to 256 KiB.

  • The canvas draws a Compose site as a group: one card per service with its state, image, public URL and volumes, and depends_on arrows.
  • The Services tab lists each service’s state and health, image digest, ports, restarts, CPU and memory, with Restart and Logs actions.
  • Settings → Compose edits the source (files and profiles for repository stacks), the services table, the public services and their health check paths, shows the Falak adjustments and, for inline files, history and diff.
  • ⋯ → Save as template turns the site into a custom template.

Container logs reach Loki with service.name=<slug> and the Compose service name; filter by service in the Logs tab. docker stats feeds CPU, memory and network metrics per container.

  • Repository stacks ship the files they mount or read with each release (at most 200 files / 2 MB). Inline files and templates ship only compose.yaml and .env.
  • include and extends work for repository stacks (files from the repository only); inline files and templates can’t use them.
  • A failed first deployment has nothing to roll back to; the containers stay as docker compose up left them (so you can read their logs).
  • Stateful apps on several servers each get their own volumes; Falak warns when a stateful template is deployed to more than one server.
  • Processes from the Processes tab (workers, cron) are not run for Compose sites; add them as Compose services.