What an installation is
Seven services, four arrangements, and one Compose file that chooses between them.
An AlgoJudge installation is a Docker Compose stack. It is delivered as its own
repository, AlgoJudge-Ops, which you clone, configure through a .env file,
and run.
That repository holds no application code and builds nothing. Every image is
pulled by tag from GHCR, which is what makes an update docker compose pull and
a rollback a digest. The Compose files that build from source live in the
product repositories and are labeled as development there; they are not this.
`0.2` is the tag an installation asks for
The eight images are published and public: docker compose pull needs no
registry login, from anywhere. What .env.example asks for is 0.2 — the
minor line this stack is written for, which takes every later patch on that
line and nothing else. Not the moving major 0: below 1.0 a minor may
change what the one before it did, so 0 would carry an installation across a
break without asking. Update and rollback says how to pin
a version more tightly still.
The services
Seven services under seven profile names. The Server and the Client carry two each, which is what lets one half be brought up without the other; the two Runners share one.
| Service | Profiles | What it is |
|---|---|---|
postgres | data | PostgreSQL 18. No host port, in any arrangement |
server | app, server | the API at /api/v1, published on 127.0.0.1:8080 |
client | app, client | the browser application, published on 127.0.0.1:8082 |
nginx | edge | TLS, one origin for both halves, ports 80 and 443 |
runner-1, runner-2 | runner | one image, two identities, and the whole fleet. Each evaluates one submission at a time in sibling containers, several of that submission's tests at once |
external-runner | external-runner | forwards them to an outside archive instead. Not in the default set — see An external judge |
What has to fit the host is lanes, not Runners. A Runner judges as many of
one submission's tests at once as RUNNER_TESTS_AT_ONCE says — one lane each —
and a lane is one core's worth of work, so widening a Runner asks of the host
exactly what adding one would. More lanes than physical cores does not judge
faster and does stop judging accurately, which is why a host with capacity to
spare raises RUNNER_TESTS_AT_ONCE rather than gaining a third Runner. What
the host needs has the table.
Three Compose networks carry them, so that the database is not reachable from the edge and evaluation traffic is separable from HTTP:
The Server is the only service on all three. PostgreSQL sits on backend alone
and has no host port in any arrangement. Only nginx publishes a port to the
network; the two loopback publications above exist for curl and for a proxy
of your own.
The edge network is declared with an explicit subnet, 172.28.0.0/24 by
default. That is not tidiness: the Server refuses to start until it has been told
whose word to take for a visitor's address, and a subnet Docker picked at random
could not be named.
The four arrangements
Chosen with COMPOSE_PROFILES in .env. No Compose file is edited for any of
them.
COMPOSE_PROFILES | ||
|---|---|---|
| T1 | edge,app,data,runner | one host, the whole product. The default |
| T2 | edge,app,data, plus runner elsewhere | Runners on their own machines |
| T3 | app,data | your own reverse proxy in front |
| — | client,server,data | one half at a time, for debugging and staged updates |
external-runner is an addition to any of them, not a further arrangement.
Add it beside runner, or give it a machine of its own; it is outside the
default set because it signs in to an outside archive under an account there —
An external judge says what it needs and what it
cannot do for you.
A Runner may live on another machine entirely
Each Runner host clones the same repository, checks out the release the
application host runs, and runs COMPOSE_PROFILES=runner with two values in its
.env:
SERVER_URL=https://your.domain
RUNNER_NAME_PREFIX=lab-a # different on every hostThat host needs Docker Engine 26 or later (Podman 5), and disk where Docker keeps its volumes: the Runners' cache and scratch live on the machine that judges, not on the application host. Below that engine a Runner refuses to judge, and there is no arrangement that avoids it — see Where the Runners keep their bytes.
The prefix names each Runner on the host — lab-a-1 and lab-a-2 — and has to
differ per host, or two machines' Runners appear in the panel under one set of
names.
A Runner opens every connection itself. It needs no inbound port and works
from behind a home router with no address of its own. It has no depends_on
on the Server in compose.yaml, deliberately, because on a Runner-only host
there is no Server service to depend on — it retries its own connection instead.
Each Runner registers separately and each needs its own approval, so a two-Runner host is two approvals.
T2 is the recommended arrangement for a public installation
Access to the Docker socket is equivalent to root on the host, and the Runner needs that socket to start job containers. In T1 the same host also holds the database, so anybody who escapes a job container is on the machine holding every submission and every password hash. On a Runner-only host the worst case is a machine that holds nothing.
What runs beside the stack, not inside it
An installation needs an OIDC provider, and neither supported identity
deployment is a service in compose.yaml. It runs beside this stack, at its
own hostname. Identity is the chapter for choosing and
registering one.
Where to go next
| What the host needs | Docker, cgroup v2, disk, logs |
| First install | from an empty directory to a verdict |
| Pre-configuration | standing one up without clicking through the panel |
| Identity | Keycloak or Authentik, and how to register one |
| Connecting a course platform | LTI 1.3, and the browser setting it turns on |
| Where the files go | the three stores, and moving between them |
| Maintenance mode | taking it out of service without stopping it |
| Backup | what a dump covers, and what it carries |
| Restore | putting one back |
| Update and rollback | and what a rollback cannot undo |
| Routine work and the schedule | garbage collection, cron, and what nobody tells you |
| Your own reverse proxy | the five rules |
| An external judge | forwarding problems to an outside archive |
Every setting in .env | the whole of it, in one table |
| When something does not work | the failure modes that look like other problems |
The source is in AlgoJudge-Ops. This project is licensed under MIT. See LICENSE. This documentation is CC BY 4.0.