Start the stack with one script and a generated .env file
Context and Problem Statement
Agoranomos promises a stack that starts with one command on a machine with Docker. Before the first start, every internal secret has to exist: the passwords of the databases, of their roles and of the message broker; the keys with which Airflow and Metabase encrypt the connections they save, and with which Airflow signs its sessions and tokens; and the passwords of the admin accounts. No secret may have a default value. The machine also needs enough memory, CPUs and disk, and the ports of the stack must be free; when they are not, the stack fails late and in ways that are hard to read. People run the stack on macOS, on Linux servers and on Windows through WSL2.
How do people start the stack, and where do its settings and secrets live?
Decision Drivers
- Docker is the only prerequisite.
- No secret has a default value, and nobody has to make one up.
- Problems with the machine show up before the start, with a message that says how to fix them.
- Plain
docker composekeeps working for people who know it. - The settings live in one file that is easy to read, to back up and to keep in a password manager.
Considered Options
- A POSIX shell script,
./agoranomos, and a.envfile that it generates from.env.example maketargets and a generated.envfile- A command-line tool in Python, installed on the host
- Docker secrets in files instead of a
.envfile - Plain
docker composewith a.envfile written by hand
Decision Outcome
Chosen option: "A POSIX shell script, ./agoranomos, and a .env file that it generates from .env.example", because it needs nothing but Docker and the standard tools of macOS, Linux and WSL2, it generates every secret, and it checks the machine before the stack starts.
In practice this means:
- Commands.
./agoranomos initcreates.envfrom.env.example, generates every secret that is empty, and creates the extensions folder when it is missing (ADR-0013).preflightchecks the machine, andupruns both, starts the stack and waits until every service is healthy. Other commands stop the stack and show its state, logs and admin logins;./agoranomos helplists them. - Secrets.
initgenerates random secrets, never changes a value that is already set, and adds the settings that a new release brings in.env.example. Only the owner can read.env(mode 600), and it is never committed. - Fail fast.
compose.yamlrequires every secret (${VAR:?…}), sodocker composestops with a hint to run./agoranomos initwhen one is missing. - Plain Compose.
.envalso setsCOMPOSE_PROJECT_NAMEandCOMPOSE_PROFILES, sodocker composecommands work afterinitwithout extra options. - Preflight. The start stops when Docker or Docker Compose is missing or older than the minimum versions (as of October 2026, Docker Engine 25.0 and Compose 2.24), when Docker has less than 6 GB of memory or fewer than 2 CPUs, when less than 10 GB of disk is free, when the CPU is neither arm64 nor x86-64 with x86-64-v2, or when a port of the stack is taken. Below the recommended 8 GB, 4 CPUs and 20 GB it warns. Preflight also warns about a Docker Compose that loses track of the image that
upbuilds for Airflow (2.37 or 2.38, or older than 5.4 with the containerd image store), because the Airflow containers then restart at every start, or get an updated image only at the next one; the stack works, so this only warns. The resource figures follow the memory budget of ADR-0010, which is a target until the stack is measured, and they change with it.--forceturns the stops on resources into warnings. - Windows is supported through WSL2.
The other options were not chosen for these reasons:
- make. As of October 2026, it is not installed by default on Ubuntu Server or on Windows.
- A Python tool on the host. It needs Python on the host, and the Python of the host differs from machine to machine.
- Docker secrets in files. Each service reads them in its own way (
_FILEvariables for PostgreSQL and Metabase,_CMDvariables for Airflow), not every secret can come from a file (as of October 2026, the_FILEvariables of Metabase do not cover its encryption key), and every file has to be readable by the user inside its container. The gain is small, because whoever can rundocker inspectalready controls Docker, which amounts to root on the host. - A
.envfile written by hand. People would pick weak secrets or copy them from examples.
Consequences
- Good, because one command starts the stack on a clean machine, with no default passwords.
- Good, because problems with the machine are reported before anything starts.
- Good, because the stack stays a standard Compose project.
- Bad, because the secrets are environment variables of the containers:
docker inspectshows them to whoever controls Docker, and the processes in a container can read them. - Bad, because
.envhas to be kept safe: without its keys, the connections saved in a restored backup of Airflow or Metabase cannot be decrypted.initand the documentation are to ask people to keep a copy in a password manager. - Bad, because a shell script is harder to test than Python code. Its tests are to run it against stand-ins for
docker,lsofanddf. - Bad, because Windows without WSL2 is not supported.
More Information
- Implementation status, as of October 2026: in place for local mode and server mode:
./agoranomoswithinit(andinit --server <domain>),preflight,up,down,status,logs,metadata,credentials,backup,restoreandhelp,.env.example, and the Compose files.credentialsshows the admin logins and the settings of.envthat hold their passwords, never a password. The measurements of the stack keep the resource figures of preflight (System requirements). Server mode and its backups are in place (ADR-0019, ADR-0020). - Docker Compose reads
.envin the project directory both for variable substitution and for its own settings, such asCOMPOSE_PROFILES(pre-defined environment variables). - Other self-hosted projects keep their secrets in a dotenv file too: Plausible Community Edition and Supabase in
.env, and Mastodon in.env.production. - The memory budget is set in ADR-0022, the supported architectures in ADR-0010, and the two modes in ADR-0011.