Skip to main content
Version: 1.1

Installation

All you need on the host is Docker with the Compose plugin. Fadebox runs as two containers: the app and a PostgreSQL database.

1. Create the compose file​

Save this as docker-compose.yml:

# Fadebox — app + PostgreSQL. Create .env next to this file first (step 2).
#
# Nothing host-specific is baked in here: the Docker socket path and the GID that owns it
# come from .env, because they differ per host (docker group on Linux, root on Docker
# Desktop, your own uid under rootless Docker).
services:
db:
image: postgres:18
environment:
POSTGRES_DB: fadebox
POSTGRES_USER: fadebox
POSTGRES_PASSWORD: ${DB_PASSWORD:?set DB_PASSWORD in .env}
# No published port: the app reaches it as db:5432 on the compose network, and a published
# port bypasses the host firewall (ufw, firewalld). For a shell or a dump, go through the
# container: `docker compose exec db psql -U fadebox fadebox`.
volumes:
# PG18 images store data in a version-specific subdir under /var/lib/postgresql
- fadebox-db:/var/lib/postgresql
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U fadebox -d fadebox" ]
interval: 5s
timeout: 5s
retries: 10

app:
image: fadebox/fadebox:1.1
depends_on:
db:
condition: service_healthy
# The image's own uid (185, UBI convention); only the supplementary group varies per host.
user: "${FADEBOX_UID:-185}:${FADEBOX_GID:-0}"
group_add:
# GID that owns the Docker socket — see step 2 for how to find it.
- "${DOCKER_GID:?set DOCKER_GID in .env}"
environment:
# Point the baked-in postgres datasource at the db service.
QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://db:5432/fadebox
QUARKUS_DATASOURCE_USERNAME: fadebox
QUARKUS_DATASOURCE_PASSWORD: ${DB_PASSWORD:?set DB_PASSWORD in .env}
# Session-cookie encryption key; generate with `openssl rand -base64 32`.
QUARKUS_HTTP_AUTH_SESSION_ENCRYPTION_KEY: "${FADEBOX_SESSION_KEY:?set FADEBOX_SESSION_KEY in .env}"
# At-rest encryption key for stored credentials; generate the same way. Keep it stable —
# changing it makes every stored credential unreadable until re-entered.
FADEBOX_ENCRYPTION_KEY: "${FADEBOX_ENCRYPTION_KEY:?set FADEBOX_ENCRYPTION_KEY in .env}"
# Bare-clone cache for git value sources (kept on a volume so fetches stay incremental)
FADEBOX_GIT_CACHE_DIR: /data/git
volumes:
- ${DOCKER_SOCK:-/var/run/docker.sock}:/var/run/docker.sock
- fadebox-git:/data
ports:
- "${FADEBOX_PORT:-8080}:8080"

volumes:
fadebox-db:
fadebox-git:

The image tag 1.0 is the release line: it moves to each 1.0.x patch as it ships and never to the next minor, so docker compose pull picks up fixes without a version jump. Every release also carries its exact tag (1.0.0) for an installation that wants nothing to move on its own; latest follows the newest release of any line, which is the one to avoid in a compose file that is meant to stay put.

2. Create .env​

Nothing host-specific is baked into the compose file, so it reads these from .env next to it (compose fails fast with a named variable if one is missing):

{
echo "DOCKER_GID=$(stat -c '%g' /var/run/docker.sock)" # macOS: stat -f '%g'
echo "FADEBOX_SESSION_KEY=$(openssl rand -base64 32)"
echo "FADEBOX_ENCRYPTION_KEY=$(openssl rand -base64 32)"
echo "DB_PASSWORD=$(openssl rand -base64 24)"
} >> .env

DOCKER_GID is the group that owns the Docker socket — docker on most Linux hosts, 0 on Docker Desktop, your own gid under rootless Docker (which also needs DOCKER_SOCK=/run/user/$UID/docker.sock). The app container keeps its own uid and just joins that group; without it, every deploy fails with permission denied on /var/run/docker.sock.

FADEBOX_ENCRYPTION_KEY encrypts stored credentials (registry passwords, git tokens, mTLS client keys, OIDC client secrets) at rest, so a database dump or backup does not leak them. Keep it stable and include it in your backups: unlike FADEBOX_SESSION_KEY (rotating that one just signs everyone out), changing this key makes every stored credential unreadable until re-entered.

3. Start it​

docker compose up -d

4. Sign in​

Open http://localhost:8080. The initial admin user's generated password is printed once in the log:

docker compose logs app | grep "generated password"

Sign in with it. Fadebox then requires a new password before the account can do anything else — a password somebody (or something) else chose is treated as temporary, so the first screen after this sign-in is the password form and every other page answers 403 until you are through it. The same applies to any account an admin creates or resets. See Passwords.

warning

Fadebox's app container mounts /var/run/docker.sock — managing a Docker daemon is effectively root on that host, so treat Fadebox admin access accordingly.

warning

This publishes Fadebox on plain HTTP on port 8080, which is fine on a laptop and not fine anywhere else: sign-in posts a password, and the session cookie is only marked Secure when the request arrives as https. For anything beyond a local trial, put a TLS-terminating reverse proxy in front and have it send X-Forwarded-Proto — see Reverse proxy.

Which version you are running​

The version sits in the sidebar footer, under your account, on every page — hover it for the date that build was made, and CLI beside it downloads the command-line client this server was released with. Administration → License shows the same two facts as a row, directly above the license's update horizon, which is checked against that build date; see Licensing.

The CLI reports its own build the usual way:

fadebox --version

Quote the server's version in any support conversation — it is the first thing that decides whether a behaviour is a bug or a release you have not upgraded to yet.

Next​

Deploy your first environment — ingress domain, a template from the catalog, and a running URL.

Optional, whenever you need them:

  • Remote runtimes to keep this VM small and deploy environments onto a powerful Docker host instead.
  • Single sign-on against your identity provider, configured in the UI.
  • Users, groups and roles for anything beyond the bootstrap admin.
  • Reverse proxy if you terminate TLS in front of Fadebox — the headers it needs from your proxy, and the one setting that stops them being spoofed.
  • Configuration reference for the environment variables the app understands.
  • Container logs — Docker keeps every line a container ever wrote until the daemon is told otherwise; two keys in daemon.json cap it.
  • Upgrading when the next release ships — what to check first, and the release notes that say what changed.