What an installation is
Nine 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 labelled as development there; they are not this.
`0` 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 — the
moving major, which every 0.x release moves, breaking changes included.
Update and rollback says what that costs and how to pin a
version instead.
The services
Nine services, and seven profile names — the Server and the Client each carry two, which is what lets one half be brought up without the other.
| 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-4 | runner | one image, four identities. Each evaluates one submission at a time in sibling containers |
external-runner | external-runner | forwards them to an outside archive instead. Not in the default set — see An external judge |
Four is the reference, not a minimum. It suits eight physical cores and leaves four for everything else; on a smaller host run fewer, because more Runners than physical cores does not judge faster and does stop judging accurately. 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 fifth one. 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 and runs COMPOSE_PROFILES=runner
with three values in its .env:
SERVER_URL=https://your.domain
RUNNER_NAME_PREFIX=lab-a # different on every host
RUNNER_WORK_DIR=/srv/algojudge/runner-workThe prefix names all four — lab-a-1 to lab-a-4 — 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 domestic 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 four-Runner host is four 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.