Install Falak (production)
Falak runs as a Docker Compose stack on a single Linux host. The installer sets up Docker, writes a generated configuration to /opt/falak/.env, starts the stack and creates the first administrator. Day-2 operations use falak-ctl.
internet ──:80/:443──► edge (Caddy) ├─ falak.example.com Let's Encrypt ─► control-plane (FrankenPHP) · reverb (websockets) ├─ agents.falak.example.com Fleet-CA cert + client-cert (mTLS) check ─► agent-api ├─ registry.falak.example.com Let's Encrypt + basic auth ─► registry (built-in image registry) └─ grafana.falak.example.com Let's Encrypt ─► grafana (optional)control-plane · agent-api · horizon · reverb · scheduler ─► postgres 17 · valkeybuilder (falak-builder serve: PHP/Composer, Node, Bun) ─► edgeBefore you begin
Section titled “Before you begin”- A host that meets the requirements: Ubuntu 22.04/24.04 or Debian 12, amd64 or arm64, 4 GB RAM (8 GB with observability), ports 80 and 443 free.
- DNS records for
falak.example.com,agents.falak.example.comandregistry.falak.example.com(plusgrafana.with observability) pointing at the host.
Install
Section titled “Install”curl -fsSL https://falak.sh/install.sh \ | sudo bash -s -- --domain falak.example.com --email you@example.comPin a release by using that release’s copy of the script:
curl -fsSL https://github.com/OthmanHaba/falak/releases/download/v0.2.6/install.sh \ | sudo bash -s -- --domain falak.example.com --email you@example.comWhat the installer does
Section titled “What the installer does”- Preflight: checks root, OS, CPU architecture, RAM, disk, that ports 80/443 are free, and that DNS for the panel and
agents.(andgrafana.with--observability) points at this host’s public IP. It prints missing records. - Installs Docker Engine and the Compose plugin from Docker’s official apt repository if missing.
- Downloads
falak-deploy.tar.gz, verifies it againstSHA256SUMS, unpacks it to/opt/falak/{deploy,observability}, and installsfalak-ctlto/usr/local/bin. - Generates
/opt/falak/.env(mode 600):APP_KEY, database and Valkey passwords, Reverb keys, the builder token, the OTLP token, the registry credentials and allFALAK_*URLs. - Pulls
ghcr.io/<owner>/falak-{control-plane,builder,edge}:<version>, starts the stack and waits until every service is healthy (up to 10 minutes). Migrations run in thecontrol-planeservice on start. - With
--observability, creates a Grafana service account token for Falak. - Creates the first administrator and prints the password once.
Options
Section titled “Options”Every option can also be set with the environment variable in the second column.
| Option | Env | Default | Meaning |
|---|---|---|---|
--domain NAME |
FALAK_DOMAIN |
required | Panel domain; agents use agents.NAME |
--email ADDRESS |
FALAK_EMAIL |
required | Let’s Encrypt account and first admin e-mail |
--admin-email ADDRESS |
FALAK_ADMIN_EMAIL |
--email |
First admin, if different |
--version TAG |
FALAK_VERSION |
latest release | Release to install |
--observability |
FALAK_OBSERVABILITY=1 |
off | Also run Grafana, Loki, Tempo, VictoriaMetrics (grafana.NAME) |
--no-observability |
FALAK_OBSERVABILITY=0 |
Turn it off on a re-run | |
--tls acme|internal |
FALAK_TLS |
acme |
internal = Caddy’s local CA, testing only |
--repo OWNER/NAME |
FALAK_REPO |
OthmanHaba/falak |
GitHub repository of the release (forks) |
--image-prefix PREFIX |
FALAK_IMAGE_PREFIX |
ghcr.io/<owner> |
Image registry prefix |
--build-from-source |
FALAK_BUILD_FROM_SOURCE=1 |
off | Build images locally instead of pulling them |
--ref REF |
FALAK_REF |
--version or main |
Git ref for --build-from-source |
--source-dir PATH |
FALAK_DEPLOY_SOURCE |
Use deploy files from a local checkout (offline/testing) | |
--http-port N / --https-port N |
FALAK_HTTP_PORT / FALAK_HTTPS_PORT |
80 / 443 |
Other ports only with --tls internal (ACME needs 80/443) |
--dir PATH |
FALAK_DIR |
/opt/falak |
Install directory |
--skip-dns-check |
FALAK_SKIP_DNS_CHECK=1 |
off | Do not require DNS to point at this host |
--force |
off | Continue on an unsupported OS or low resources | |
-h, --help |
Print usage |
Re-running
Section titled “Re-running”Running the installer again is safe: secrets in .env are kept, settings from the flags are updated, the stack is converged, services whose mounted config files changed are recreated, and the admin is not created twice. Use it to enable observability later:
curl -fsSL https://falak.sh/install.sh \ | sudo bash -s -- --domain falak.example.com --email you@example.com --observability/opt/falak/.env settings + secrets (install.sh; never commit or share)/opt/falak/custom.env optional extra app env (GITHUB_APP_*, mirrors, FALAK_* tuning) loaded by the app containers/opt/falak/deploy/ compose.yml, falak-ctl, image support files (replaced on update; previous kept as deploy.prev)/opt/falak/observability/ Loki/Tempo/Grafana/gateway configs/opt/falak/backups/ falak-ctl backup output/usr/local/bin/falak-ctl the operations toolSee Configuration for what goes in .env versus custom.env.
After installing
Section titled “After installing”- Log in at
https://falak.example.comwith the printed password. - Configure mail so invitations and e-mail alerts work.
- On a panel reachable from the internet, restrict sign-up with
FALAK_REGISTRATION=inviteorclosed(anyone can create an account by default). - Schedule backups and copy them off the host.
- Read Security hardening.
- Connect servers. Agent binaries come from your panel (
/install/agent/linux-{amd64,arm64}), not from GitHub.
The built-in image registry
Section titled “The built-in image registry”The stack runs an image registry (Docker Distribution) that the edge serves at https://registry.<domain> with a Let’s Encrypt certificate and basic auth. Docker-mode builds (Dockerfile sites, Compose services with build:) push there, and servers pull from it, pinned by digest. Only the edge can reach the registry container; images live in the registry-data volume.
- The installer generates the settings into
.env:FALAK_REGISTRY_HOST,FALAK_REGISTRY_URL,FALAK_REGISTRY_USERNAME(falak) andFALAK_REGISTRY_PASSWORD. Falak hands the credentials to builders and servers itself. - Create the
registry.<domain>DNS record like the others.falak-ctl registry statusshows its address and size and checks that it answers with its credentials;falak-ctl doctoralso checks that it refuses requests without them. - Builder servers and external builders push to
https://registry.<domain>over the internet, so they need to resolve and reach it. With--tls internal, Docker on other hosts doesn’t trust the registry’s certificate: Docker builds need--tls acme(or Caddy’s internal root trusted in each Docker daemon). - Installs from before the registry get
FALAK_REGISTRY_*added to.envby the nextfalak-ctl uporfalak-ctl update(after an update run by an older falak-ctl, runfalak-ctl uponce). Then add theregistry.DNS record and check it withfalak-ctl registry status.
Registry storage
Section titled “Registry storage”Every Docker build pushes an image. Two jobs keep the registry from growing forever:
- Daily cleanup of old images (
falak:registry-prunein the control plane, 03:45, after the artifacts prune): deletes the images of builds whose artifact was pruned (each site keeps its newestFALAK_ARTIFACTS_KEEPbuilds, default 10). An image stays while a release may still run it (pending, live or kept for rollback), while its build is running or less than a day old; tags that aren’t build ids are never touched. Images of a deleted site’s builds goFALAK_REGISTRY_DELETED_SITE_GRACE_DAYS(default 7) days after the build. Preview withfalak-ctl registry prune --dry-run, or run it now withfalak-ctl registry prune. - Weekly garbage collection (
/etc/cron.d/falak-registry-gc, Sunday 04:17, written byfalak-ctl upandupdate):falak-ctl registry gcdeletes the layers nothing references any more, which is what frees disk space. The registry is stopped while it runs (a push during garbage collection could lose layers), so it runs at night and is skipped (logged, tried again the next week) while an image build is queued or running, or when the control plane can’t tell.--forceruns it anyway. Output goes to/var/log/falak-registry-gc.log. SetFALAK_REGISTRY_GC=0in.envand runfalak-ctl upto remove the cron entry.
Testing locally with internal TLS
Section titled “Testing locally with internal TLS”--tls internal issues certificates from Caddy’s local CA, skips the DNS check and allows custom ports:
sudo bash install.sh --domain falak.test --email you@example.com --tls internal --http-port 8080 --https-port 9443curl -k --resolve falak.test:9443:127.0.0.1 https://falak.test:9443/upBuild images yourself
Section titled “Build images yourself”docker build -f control-plane/Dockerfile --build-arg FALAK_VERSION=dev -t falak-local/falak-control-plane:dev .docker build -f deploy/builder.Dockerfile --build-arg FALAK_VERSION=dev -t falak-local/falak-builder:dev .docker build --build-arg FALAK_VERSION=dev -t falak-local/falak-edge:dev deploy/edgeOr let the installer do it with --build-from-source [--ref REF].
Troubleshooting
Section titled “Troubleshooting”| Symptom | Fix |
|---|---|
DNS does not point at this host |
Create the printed A/AAAA records and wait (dig +short falak.example.com). With Cloudflare, use DNS only. |
port 80 is in use |
Stop the other web server: systemctl disable --now nginx apache2 caddy. |
pulling images … failed |
The release is not published, or GHCR packages are private (docker login ghcr.io). |
the stack did not become healthy |
The installer prints the last logs; then falak-ctl doctor, falak-ctl logs control-plane. |
FALAK_EDGE_SUBNET … overlaps |
Pick another private /24 in .env (FALAK_EDGE_SUBNET=…) and re-run. The app trusts proxy headers only from that subnet. |
Docker build fails at push (lookup registry.falak.local … no such host, 401, x509) |
The install has no built-in registry yet, or its DNS record is missing: run falak-ctl up (adds FALAK_REGISTRY_*), create the registry.<domain> record, then falak-ctl registry status. x509 with --tls internal: see the built-in image registry. |
| Browser shows a certificate error | falak-ctl logs edge for ACME errors. Ports 80/443 must be reachable from the internet. Let’s Encrypt rate limits apply to repeated reinstalls. |