# Deploy with Docker Compose

> Deploy a Docker Compose stack with Falak from your repo or pasted inline — HTTPS for public services, named volumes, leader-only commands and rollbacks.

Source: https://falak.sh/docs/deploy/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](/docs/templates/deploy-a-template/) are Compose sites too.

## Prerequisites

- 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:`.

## Choose the source

| 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 |

## Deploy

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](/docs/guides/compose-apps/).

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

```json title="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"}
}
```

## How Falak renders your Compose file

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](/docs/guides/compose-apps/#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**.

## The deploy flow

```text title="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 `healthcheck`s so `--wait` means "ready". Raise the wait with `FALAK_COMPOSE_WAIT_TIMEOUT` (seconds) for slow starters.

### Run a command once, on the leader

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):

```yaml title="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.

## Domains for public services

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

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](/docs/guides/compose-apps/#edge-for-every-public-service).

## Security policy

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.

## In the UI

- 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](/docs/templates/custom-templates/).

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.

## Limits

- 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.

## Next steps
