DOMjudge 9 on Ubuntu 24.04 on Azure User Guide
Overview
DOMjudge is the widely-used, open source automated programming-contest control system behind ICPC-style contests, university courses and practice sessions. Contestants submit source code; a sandboxed judgehost compiles and runs each submission against secret test data and grades it automatically as accepted or rejected, while a live scoreboard tracks the standings. It ships separate interfaces for the jury/admin, for teams, and for public spectators, plus a full REST API.
The cloudimg image delivers DOMjudge 9.0.1 as a complete all-in-one appliance on Ubuntu 24.04: the Symfony/PHP domserver (served by nginx + PHP 8.3-FPM, backed by MariaDB) and a local judgehost (judgedaemon) preconfigured on a single VM, so a correct submission is graded end to end the moment it boots. A demo contest with example problems is pre-loaded so you can try a real submission straight away. Backed by 24/7 cloudimg support.
What is included:
- DOMjudge 9.0.1 (GPL-2.0), built from the official release, installed under
/opt/domjudge - Combined domserver + judgehost on one VM: nginx + PHP 8.3-FPM + MariaDB, plus the
create-cgroupsanddomjudge-judgedaemon@0services - A hardened judging sandbox:
runguardruns untrusted contestant code inside a debootstrap chroot with cgroup v2 memory + CPU limits (no swap is baked into the image) - A pre-loaded demo contest with the example problems (
hello,boolfind,fltcmp) and a demo team - Per-VM secrets generated at first boot — the web
adminpassword, thejudgehostREST credential, thedemoteam password, the MariaDB password and the SymfonyAPP_SECRET— with no default login; written to a root-only file - Health via the public scoreboard at
/publicand the REST API at/api/v4/ - 24/7 cloudimg support

Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2ms (2 vCPU / 8 GiB RAM) is a good starting point for a small contest or a classroom; scale up, or attach additional dedicated judgehosts, for larger contests or higher submission volume (see Scaling the judge below). NSG inbound: allow 22/tcp from your management network and 80/tcp (plus 443/tcp once you enable HTTPS) from your users.
Step 1 — Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for DOMjudge by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size (Standard_B2ms or larger); under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create → Create.
Step 2 — Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name domjudge \
--image <marketplace-image-urn> \
--size Standard_B2ms \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name domjudge --port 80 --priority 1010
Step 3 — Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 — Confirm the services are running
DOMjudge runs as five systemd units — MariaDB, PHP-FPM and nginx (the domserver), plus create-cgroups and domjudge-judgedaemon@0 (the judgehost). They should all report active:
for u in mariadb php8.3-fpm nginx create-cgroups 'domjudge-judgedaemon@0'; do
printf '%-28s %s\n' "$u" "$(systemctl is-active $u)"
done

The public scoreboard answers on port 80 without authentication, and the REST API lists the contests:
curl -s -o /dev/null -w 'public scoreboard: HTTP %{http_code}\n' http://127.0.0.1/public
curl -s http://127.0.0.1/api/v4/contests | head -c 200; echo

Step 5 — Retrieve your per-VM credentials
Every DOMjudge secret is regenerated uniquely on first boot — there is no default login. Read them (root only):
sudo cat /root/domjudge-credentials.txt
You will see the admin web password, the demo team password, the MariaDB password and the site URL. Store them somewhere safe.

Step 6 — Sign in to the jury interface
Open http://<vm-public-ip>/ in a browser and sign in as admin with the password from the credentials file. The jury interface is where you manage contests, problems, teams, clarifications and judgings.

Step 7 — Submit a solution and watch it get judged
The image ships a demo contest with the example hello problem and a demo team, so you can prove the autograder end to end immediately. Submit the shipped accepted solution over the REST API as the demo team (substituting the demo password from your credentials file):
ADMIN_PASS='<DOMJUDGE_ADMIN_PASSWORD>'
DEMO_PASS='<DOMJUDGE_DEMO_PASSWORD>'
SOL=/opt/domjudge/domserver/example_problems/hello/submissions/accepted/test-hello.c
SUBID=$(curl -s -u "demo:${DEMO_PASS}" \
-F "problem=hello" -F "language=c" -F "code[]=@${SOL}" \
http://127.0.0.1/api/v4/contests/demo/submissions | python3 -c 'import sys,json;print(json.load(sys.stdin)["id"])')
echo "submitted id=${SUBID}; waiting for the judgedaemon..."
for i in $(seq 1 40); do
V=$(curl -s -u "admin:${ADMIN_PASS}" \
"http://127.0.0.1/api/v4/contests/demo/judgements?submission_id=${SUBID}" \
| python3 -c 'import sys,json;d=json.load(sys.stdin);print(d[-1].get("judgement_type_id") or "" if d else "")')
[ -n "$V" ] && { echo "verdict: $V"; break; }
sleep 3
done
A correct solution returns the verdict AC (accepted). In the jury interface the same submission shows a green CORRECT judging, with the compile result and per-testcase run times.

The result appears on the public scoreboard at http://<vm-public-ip>/public:

Over the REST API the judgement carries judgement_type_id: "AC":

Step 8 — Run your own contest
From the jury interface you can create your own contest, import problems (DOMjudge accepts Kattis/ICPC problem-archive ZIPs), register teams and issue their passwords, then open the contest. The admin CLI is available for scripting:
sudo -u www-data /opt/domjudge/domserver/bin/dj_console list | grep domjudge
Reset a user's password from the CLI at any time:
sudo -u www-data /opt/domjudge/domserver/bin/dj_console domjudge:reset-user-password <username> <new-password>
The judging sandbox, memory limits and swap
The judgehost compiles and runs untrusted code inside a chroot with cgroup v2 resource limits enforced by runguard. This image deliberately ships with no swap (an Azure Marketplace image requirement); the per-submission memory limit is enforced directly by the memory controller, and a submission that exceeds it is stopped and reported as Memory Limit Exceeded — no swap device is needed. If you run contests with large per-submission memory limits and want the extra headroom of swap, enable it on Azure's ephemeral resource disk (managed by the Azure Linux Agent) rather than on the OS disk:
# /etc/waagent.conf
ResourceDisk.EnableSwap=y
ResourceDisk.SwapSizeMB=4096
Then sudo systemctl restart walinuxagent. This keeps the OS image swap-free while giving the judge swap on the throwaway resource disk.
Scaling the judge
A single VM runs the domserver and one judgehost, which is ample for classrooms and small contests. For larger contests, run the domserver on this VM and attach additional dedicated judgehosts: install the DOMjudge judgehost on separate VMs, point their restapi.secret at http://<domserver-ip>/api with the judgehost credential from /opt/domjudge/domserver/etc/restapi.secret, and they will pull and grade submissions in parallel. Give each judgehost its own CPU cores for consistent timing.
Enable HTTPS (recommended for public contests)
Put a TLS certificate in front of nginx. With a DNS name pointed at the VM:
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com
Maintenance
- Back up the MariaDB database (
domjudge) regularly — it holds every contest, submission and judging:sudo mysqldump domjudge | gzip > domjudge-backup.sql.gz - Security updates apply automatically via
unattended-upgrades - Logs:
sudo journalctl -u domjudge-judgedaemon@0for the judge, and/opt/domjudge/domserver/webapp/var/log/for the domserver
Support
Every cloudimg image includes 24/7 support. Contact us through the cloudimg website with your VM details and the output of the commands above.