AlgoJudge documentation0.1

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

KeyDefault
COMPOSE_PROFILESedge,app,data,runnerwhich services run. See the four arrangements; external-runner is an addition to any of them
TZEurope/Warsawanchors the scheduled work to local time
REGISTRYghcr.io/algojudgewhere the images are pulled from
SERVER_TAG0a moving major, and no v. See Updating
CLIENT_TAG0
RUNNER_TAG0the Runner and its four lang-* images
EXTERNAL_RUNNER_TAG0its own version and its own repository, so its own tag
POSTGRES_TAG18the major is pinned on purpose — 18 moved the data directory
NGINX_TAG1.30-alpinenginx'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_SIZE20meach container's log, bounded by the stack itself rather than by your daemon
LOG_MAX_FILES5Docker's local driver, which compresses what it rotates

The database

KeyDefault
POSTGRES_DBalgojudge
POSTGRES_USERalgojudge
POSTGRES_PASSWORDrequired
MIGRATE_ON_STARTtruebecomes the Server's AJ_Database__MigrateOnStart, which is false on its own. Ops turns it on because it backs up first

The network

KeyDefault
HTTP_PORT80what the bundled nginx publishes
HTTPS_PORT443
SERVER_PORT8080published on loopback only
CLIENT_PORT8082published 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_URLemptyits 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_NETWORKS172.28.0.0/24whose word to take for a visitor's address. A network, not an address; none for a Server reached directly
EDGE_SUBNET172.28.0.0/24the Compose network the bundled nginx sits on
SERVER_STOP_GRACE40slong enough for the Server to answer the claims it is holding open. Raise it beside AJ_Poll__WaitSeconds if you raise that
SERVER_LOGWarningnot 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

KeyDefault
STORAGE_KINDpostgrespostgres, filesystem or s3. Only postgres makes the database dump the whole of the installation's state
STORAGE_PATH/var/lib/algojudge/objectsfilesystem only, in a volume of its own. Back it up in step with the database
STORAGE_ENDPOINTemptys3 only, and all four below are required together
STORAGE_BUCKETempty
STORAGE_ACCESS_KEYempty
STORAGE_SECRET_KEYempty
AJ_ADMIN_TOKENrequiredbecomes AJ_Admin__Token. Set it before the first start

The Runner

KeyDefault
RUNNER_WORK_DIRrequiredan 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_PREFIXrunnerwhat 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_CPUSETRUNNER_4_CPUSETemptywhich 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_TYPESstandard-io@1,output-only@1what this Runner offers to evaluate. A value that names nothing — a stray comma — is refused at startup
RUNNER_TAGSemptypools. Read at the first registration only; naming one takes the Runner out of the general queue
RUNNER_STOP_GRACE30show 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_LOGinfobecomes RUST_LOG
DOCKER_GID999the 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_URLhttp://server:8080only 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.

KeyDefault
EXTERNAL_JUDGE_USERNAMErequired with the profilethe account at the archive. Every forwarded submission is made under it and stays on it
EXTERNAL_JUDGE_PASSWORDrequired with the profilerefused 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_JUDGEuvawhich archive. One process serves one judge; a second is a second service
EXTERNAL_RUNNER_NAMEexternal-runner-1a whole name, not a prefix: this is one Runner, with its own identity and its own approval
EXTERNAL_PROBLEM_TYPESemptyempty declares the archive's own type. A typo here is silence, not an error
EXTERNAL_RUNNER_TAGSemptypools, read at the first registration only
EXTERNAL_RUNNER_LOGinfobecomes RUST_LOG
EXTERNAL_RUNNER_STOP_GRACE60stwice 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.

KeyDefault
BACKUP_DIR./backups
BACKUP_PRESETstandardminimal, standard or extended
BACKUP_KEEP_DAILYfrom the presetoverrides one part of it
BACKUP_KEEP_WEEKLYfrom the preset
BACKUP_KEEP_MONTHLYfrom the preset
BACKUP_MIN_KEEP2never dropped, whatever the size budget says
BACKUP_MAX_TOTAL_GBwritten by preflight.sh25% of the filesystem, never more than 50%
BACKUP_FREE_SPACE_RESERVE_GB10the margin left free after a dump
BACKUP_VERIFY_FULLfalseread every block back. Costs a full decompression pass on every backup

Collecting rubbish

KeyDefault
GC_TMP_RETENTION_DAYS7how old a work directory must be before it is collected
GC_PRUNE_IMAGEStrueprune 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.

On this page