Every setting in `.env`
The whole of an installation's configuration in one table, what each key defaults to, and the three that have no default at all.
One file configures an installation: .env, beside compose.yaml. No Compose
file is edited for any supported arrangement.
The pages around this one explain the settings in the order somebody meets them.
This one is the reference: every key, in one place, so that a value you find in
somebody's .env can be looked up.
Three keys have no default and the stack will not start without them
AJ_ADMIN_TOKEN, POSTGRES_PASSWORD and RUNNER_WORK_DIR. Compose refuses to
start rather than substituting an empty string, which is deliberate: an
administrator token that quietly defaulted to nothing would leave /admin
closed and the administrator's password unreachable.
What runs, and from where
| Key | Default | |
|---|---|---|
COMPOSE_PROFILES | edge,app,data,runner | which services run. See the four arrangements; external-runner is an addition to any of them |
TZ | Europe/Warsaw | anchors the scheduled work to local time |
REGISTRY | ghcr.io/algojudge | where the images are pulled from |
SERVER_TAG | 0 | a moving major, and no v. See Updating |
CLIENT_TAG | 0 | |
RUNNER_TAG | 0 | the Runner and its four lang-* images |
EXTERNAL_RUNNER_TAG | 0 | its own version and its own repository, so its own tag |
POSTGRES_TAG | 18 | the major is pinned on purpose — 18 moved the data directory |
NGINX_TAG | 1.30-alpine | nginx's stable line. Even minors are stable, odd ones mainline. Judge a base tag by when it was last built, not by whether it pulls |
LOG_MAX_SIZE | 20m | each container's log, bounded by the stack itself rather than by your daemon |
LOG_MAX_FILES | 5 | Docker's local driver, which compresses what it rotates |
The database
| Key | Default | |
|---|---|---|
POSTGRES_DB | algojudge | |
POSTGRES_USER | algojudge | |
POSTGRES_PASSWORD | required | |
MIGRATE_ON_START | true | becomes the Server's AJ_Database__MigrateOnStart, which is false on its own. Ops turns it on because it backs up first |
The network
| Key | Default | |
|---|---|---|
HTTP_PORT | 80 | what the bundled nginx publishes |
HTTPS_PORT | 443 | |
SERVER_PORT | 8080 | published on loopback only |
CLIENT_PORT | 8082 | published on loopback only |
API_BASE_URL | / | the Server's address as a browser reaches it. / when one origin serves both. Two origins must be the same site — one registrable domain, one scheme — or nobody can sign in. See Your own reverse proxy |
APP_BASE_URL | empty | its mirror image: where a browser reaches the application. Empty is right on one origin. On two, leaving it empty ends a federated sign-in at a bare /activities, resolved against the API's origin |
TRUSTED_PROXY_NETWORKS | 172.28.0.0/24 | whose word to take for a visitor's address. A network, not an address; none for a Server reached directly |
EDGE_SUBNET | 172.28.0.0/24 | the Compose network the bundled nginx sits on |
SERVER_STOP_GRACE | 40s | long enough for the Server to answer the claims it is holding open. Raise it beside AJ_Poll__WaitSeconds if you raise that |
SERVER_LOG | Warning | not Information: at that level the Server logs every SQL statement, measured at 91–98% of everything a judging stack writes — over a gigabyte for a three-thousand-submission contest, on the database's own disk |
EDGE_SUBNET and TRUSTED_PROXY_NETWORKS must agree. The default pair does.
Changing one without the other means the Server stops believing the proxy in
front of it, and every visitor is recorded as arriving from that proxy.
Storage and the operator surface
| Key | Default | |
|---|---|---|
STORAGE_KIND | postgres | postgres, filesystem or s3. Only postgres makes the database dump the whole of the installation's state |
STORAGE_PATH | /var/lib/algojudge/objects | filesystem only, in a volume of its own. Back it up in step with the database |
STORAGE_ENDPOINT | empty | s3 only, and all four below are required together |
STORAGE_BUCKET | empty | |
STORAGE_ACCESS_KEY | empty | |
STORAGE_SECRET_KEY | empty | |
AJ_ADMIN_TOKEN | required | becomes AJ_Admin__Token. Set it before the first start |
The Runner
| Key | Default | |
|---|---|---|
RUNNER_WORK_DIR | required | an absolute host path. Letting Compose create it is right — it makes one as root, mode 0755, which the Runner writes to as root and every job container reads as uid 65534. A directory locked down by hand fails the second half and every job then reports Permission denied (os error 13). A path the daemon cannot open becomes an empty directory rather than an error |
RUNNER_NAME_PREFIX | runner | what the four register as: this with -1 to -4 after it. A second host running Runners needs a different prefix, or the panel shows two rows called the same thing |
RUNNER_1_CPUSET … RUNNER_4_CPUSET | empty | which processors each Runner may use. Empty lets the host's scheduler place every job. The division worth making is one Runner per physical core, given both of that core's threads — see What the host needs |
RUNNER_PROBLEM_TYPES | standard-io@1,output-only@1 | what this Runner offers to evaluate. A value that names nothing — a stray comma — is refused at startup |
RUNNER_TAGS | empty | pools. Read at the first registration only; naming one takes the Runner out of the general queue |
RUNNER_STOP_GRACE | 30s | how long Docker waits before killing a Runner. On SIGTERM it hands its job back so another takes it at once; shortening this below what those calls take turns a stop into a kill, and a killed Runner leaves its job to the lease. See Update and rollback |
RUNNER_LOG | info | becomes RUST_LOG |
DOCKER_GID | 999 | the group that owns the daemon's socket. 0 on Docker Desktop. Not load-bearing — the service runs as root — but preflight.sh warns when it is wrong |
SERVER_URL | http://server:8080 | only for a host with no server container; /api/v1 is appended |
The external Runner
Only read when external-runner is in COMPOSE_PROFILES. See An external
judge.
| Key | Default | |
|---|---|---|
EXTERNAL_JUDGE_USERNAME | required with the profile | the account at the archive. Every forwarded submission is made under it and stays on it |
EXTERNAL_JUDGE_PASSWORD | required with the profile | refused by preflight.sh, not by compose.yaml: Compose interpolates the whole file before it applies profiles, so a required marker here would break every arrangement that does not run this service |
EXTERNAL_JUDGE | uva | which archive. One process serves one judge; a second is a second service |
EXTERNAL_RUNNER_NAME | external-runner-1 | a whole name, not a prefix: this is one Runner, with its own identity and its own approval |
EXTERNAL_PROBLEM_TYPES | empty | empty declares the archive's own type. A typo here is silence, not an error |
EXTERNAL_RUNNER_TAGS | empty | pools, read at the first registration only |
EXTERNAL_RUNNER_LOG | info | becomes RUST_LOG |
EXTERNAL_RUNNER_STOP_GRACE | 60s | twice the sandboxing Runner's, because it hands back up to twenty pending submissions at one call each |
Its lease and its polling intervals are deliberately not offered: they check one another at startup, the image's defaults satisfy all of it, and the well-meant change — polling somebody else's service more gently — is the one that breaks it. A lease that could expire between two renewals is a submission the archive receives twice.
Backups
Covered in full on Backup; this is the key list.
| Key | Default | |
|---|---|---|
BACKUP_DIR | ./backups | |
BACKUP_PRESET | standard | minimal, standard or extended |
BACKUP_KEEP_DAILY | from the preset | overrides one part of it |
BACKUP_KEEP_WEEKLY | from the preset | |
BACKUP_KEEP_MONTHLY | from the preset | |
BACKUP_MIN_KEEP | 2 | never dropped, whatever the size budget says |
BACKUP_MAX_TOTAL_GB | written by preflight.sh | 25% of the filesystem, never more than 50% |
BACKUP_FREE_SPACE_RESERVE_GB | 10 | the margin left free after a dump |
BACKUP_VERIFY_FULL | false | read every block back. Costs a full decompression pass on every backup |
Collecting rubbish
| Key | Default | |
|---|---|---|
GC_TMP_RETENTION_DAYS | 7 | how old a work directory must be before it is collected |
GC_PRUNE_IMAGES | true | prune this project's images. Only this project's |
What is not in .env
Some settings are written into compose.yaml because an installation has no
reason to change them: the environment the Server runs as, the default storage
id, where pre-configuration is mounted, the Runner's own work path inside its
container, and the four language images, which are built from REGISTRY and
RUNNER_TAG.
The Server reads a good deal more than the keys above — the whole of it is on Server configuration. Ops sets what an installation needs and leaves the rest at the Server's own defaults.