Ubuntu 22.04 安装 DOMJudge 9.0.1 指南

在 Ubuntu 22.04 上从零源码安装 DOMjudge 9.0.1(domserver+judgehost+chroot 沙箱)的完整实测指南:PHP8.3/PPA、configure root 拒绝、unix_socket 建库、judgedaemon 绑核 systemd 托管与端到端 AC 判题验证,含在线/离线两方案对比与 8 条排障实录。

DOMJudgeDOMjudge代码评测PHP 编程Ubuntu22.042026/10/28

Ubuntu 22.04 安装 DOMJudge 9.0.1 指南

目标:在 Ubuntu 22.04 LTS 服务器上从零安装一套完整可用的 DOMjudge 9.0.1 竞赛评测系统(domserver 网站端 + judgehost 评测机 + chroot 评测沙箱),并完成比赛创建、团队提交、自动判题的端到端验证。已在 Ubuntu 22.04(PHP 8.3 + MariaDB 10.6 + nginx 1.18)环境实测通过,含全程排障实录。

一、环境信息

本文所有命令与回显均在以下真实环境实测(2026-10-28):

  • 操作系统:Ubuntu 22.04 LTS(GNU/Linux 5.15,x86_64)
  • DOMjudge:9.0.1(官方 tarball 源码安装,安装前缀 /opt/domjudge)
  • Web 服务:nginx 1.18.0 + PHP-FPM 8.3.36(ppa:ondrej/php)
  • 数据库:MariaDB 10.6.23
  • 编译器/语言:gcc/g++(12.x)、OpenJDK 11、Python 3.10
  • 角色拓扑:单机同时承担 domserver 与 judgehost(1 核绑定的 judgedaemon),生产多机拆分方式见第七节
  • 监听端口:80/tcp(nginx,DOMjudge 站点前缀 /domjudge/)
  • 关键目录:/opt/domjudge/domserver(网站端)、/opt/domjudge/judgehost(评测机)、/chroot/domjudge(评测沙箱)

版本核实说明:官方最新稳定版以 domjudge.org 首页 Download 徽章为准,2026-10-28 实查为 9.0.1。系统架构与选型背景见 《DOMJudge 介绍与选型指南》。

二、前置条件

DOMjudge 是「网站端 + 评测机」双角色系统,安装前先确认以下条件全部满足,任一缺失都会导致后续步骤中断。

  1. 一台具有 root 权限的 Ubuntu 22.04 服务器:评测沙箱(chroot 构建、系统调用限制)与 systemd 单元安装都需要 root;官网 about 页明确要求「至少一台拥有 root 权限的 Linux 机器」。
  2. PHP ≥ 8.2.0 及指定扩展:官方管理员手册对 9.0 系列要求 PHP 8.2 以上,扩展含 curl、ds、gd、intl、json、mbstring、mysqli、xml、zip。Ubuntu 22.04 官方源只有 PHP 8.1,必须先接入 ppa:ondrej/php(见 4.2 节实测排障)。
  3. MariaDB 或 MySQL:可与本机同机部署(本文采用),官方对 9.0 的要求见手册;MariaDB 10.6 实测可用。
  4. nginx 或 Apache:本文用 nginx;Apache 配置片段官方同样随包提供(etc/apache.conf)。
  5. 评测语言编译器:按需安装 gcc/g++、OpenJDK、Python3 等,它们同时要装进 chroot 沙箱(4.8 节)。
  6. sudo、debootstrap、acl 等 judgehost 依赖:judgedaemon 的 runguard 包装器依赖 sudo 提权完成 chroot 切换。
  7. 可用的软件下载渠道:官方 tarball 位于 domjudge.org,若直连缓慢可用代理(4.3 节有实测降级记录)。

确认系统版本与核心组件在位的验证命令:

lsb_release -ds && uname -m

实测回显:

Ubuntu 22.04.5 LTS
x86_64

三、版本选型说明

安装前需要确定三件事:装哪个版本、从哪里拿、装成什么形态。

项目结论(2026-10-28 实查)说明
最新稳定版9.0.1domjudge.org 首页 Download 徽章
上一稳定线8.3.2官方下载页仍提供
获取渠道https://www.domjudge.org/releases/domjudge-9.0.1.tar.gz官方 releases 目录
Ubuntu apt 仓库无 domjudge 包apt-cache policy domjudge 实测无输出
安装形态源码 tarball 编译安装官方手册唯一主线方式

两点关键差异解读:

  • apt 装不了:Ubuntu 22.04 官方源未收录 DOMjudge,apt-cache policy domjudge 无任何候选版本(实测),所以「apt install domjudge」这条路在 Ubuntu 22.04 上不存在,编译安装是唯一官方路径。
  • PHP 版本必须升级:Ubuntu 22.04 自带 PHP 8.1,不满足 9.0 系列要求的 ≥8.2.0,需通过 ppa:ondrej/php 安装 8.3。这个差异不是可选项,是硬门槛(4.2 节实测验证)。

💡 技术拓展:竞赛评测系统的「安装形态」不止编译安装一种。常见获取渠道对比——

渠道特点适用场景
源码 tarball 编译官方主线,版本最新,可控前缀生产赛事部署(本文方案)
发行版 apt 包免编译,但 Ubuntu 22.04 未收录此路不通,仅作事实说明
打包发行形态官方提供 domserver/judgehost 预打包发行物,启动快快速体验与临时赛
源码 git 检出含最新特性但有开发态依赖二次开发、贡献代码

本文实际采用「源码 tarball 编译」,理由是它是官方手册唯一完整支持的安装路径,且版本与官网稳定版严格一致。

四、在线安装方案 A(官方 tarball 源码编译)

本章是完整主线:依赖 → 下载 → 编译 → 数据库 → Web 服务 → 评测机 → 沙箱 → systemd 托管 → 端到端验证。每一步都给出实测命令与真实回显。

4.1 方案说明与整体流程

方案 A 面向可联网的服务器:从官方 releases 地址下载 9.0.1 tarball,编译出 domserver(网站端)与 judgehost(评测机)两个角色并安装到 /opt/domjudge,随后初始化数据库、配置 nginx/PHP-FPM、构建评测 chroot 沙箱,最后以 systemd 托管 judgedaemon 并完成端到端判题验证。整体顺序不可颠倒:数据库初始化依赖编译产物,chroot 依赖 debootstrap,judgedaemon 启动依赖前两者。

4.2 安装系统依赖

先安装 domserver 侧依赖(nginx、MariaDB、PHP 8.3 及扩展、composer),再补 judgehost 侧依赖(sudo、debootstrap、acl 等)。Ubuntu 22.04 的 PHP 8.1 不满足要求,第一步先接入 ppa:ondrej/php:

export DEBIAN_FRONTEND=noninteractive
add-apt-repository -y ppa:ondrej/php
apt-get update
apt-get -y install build-essential make gcc g++ autoconf automake \
  composer php8.3-cli php8.3-mysql php8.3-zip php8.3-gd php8.3-curl \
  php8.3-intl php8.3-mbstring php8.3-xml php8.3-fpm php8.3-ds php8.3-bcmath \
  nginx mariadb-server acl sudo debootstrap lsof procps \
  openjdk-11-jdk-headless default-jre-headless linux-libc-dev \
  curl wget unzip git

实测回显(节选,apt-get 输出很长,保留关键配置段与版本确认):

Setting up nginx (1.18.0-6ubuntu14.22) ...
Setting up mariadb-server (1:10.6.23-0ubuntu0.22.04.1) ...
Setting up composer (2.2.6-2ubuntu4) ...

装完立即验证三个核心组件版本,这一步同时确认 PHP 已升到 8.3:

php -v | head -1
mariadbd --version
nginx -v

实测回显:

PHP 8.3.36 (cli) (built: Oct 21 2026 14:31:50) (NTS)
mariadbd  Ver 10.6.23-MariaDB-0ubuntu0.22.04.1 for debian-linux-gnu on x86_64 (Ubuntu 22.04)
nginx version: nginx/1.18.0 (Ubuntu)

⚠️ 排障实录:若跳过 PPA 直接用 Ubuntu 22.04 源安装,得到的是 PHP 8.1(实测 PHP 8.1.2-1ubuntu2.28)。后续编译虽能通过,但 domserver 运行时会出现兼容性问题(9.0 的 webapp 基于 Symfony 6.4,要求 PHP ≥ 8.2)。正确做法是如上先加 ppa:ondrej/php 再安装 php8.3-* 全家桶,其中 php8.3-ds 与 php8.3-bcmath 是 DOMjudge 官方点名的扩展,容易漏装。

4.3 下载官方 tarball

从官方 releases 目录下载 9.0.1 源码包,包体约 55.5MB。先直连官方源,缓慢或超时再切换代理(本文实测直连速率约 100KB/s 不满足使用,切代理后约 3.8MB/s):

cd /root
curl -fLO --max-time 300 https://www.domjudge.org/releases/domjudge-9.0.1.tar.gz

实测回显(输出节选,实测首尾):

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0 55.5M    0 95517    0     0  47369      0  0:20:29  0:00:02  0:20:27 47379
 11 55.5M   11 6741k    0     0  1737k      0  0:00:32  0:00:03  0:00:29 1737k
 32 55.5M   32 18.0M    0     0  3816k      0  0:00:14  0:00:04  0:00:10 3816k

⚠️ 网络说明:实测环境直连 domjudge.org 速率仅约 100–170KB/s(预计 5 分钟以上且中途超时被截断),改走代理 http://192.168.1.62:7897 后速率升至约 3.8MB/s 并完整下载 55.5MB。当前实测可用渠道:直连慢、代理可用,内网部署请按「官方源 → 国内源 → 代理」的降级链自行选择。使用代理的命令形态:curl -fLO --max-time 300 -x http://192.168.1.62:7897 https://www.domjudge.org/releases/domjudge-9.0.1.tar.gz。

解压并进入源码目录:

cd /root
tar xzf domjudge-9.0.1.tar.gz
cd domjudge-9.0.1
ls

实测回显(节选):

CODINGSTYLE.md  CONTRIBUTING.md  CONTRIBUTORS.md  COPYING  COPYING.BSD
COPYING.MIT  ChangeLog  Makefile  Makefile.global  README.md  aclocal.m4
bootstrap  codecov.yml  config.guess  config.sub  configure  configure.ac
coverity  doc  etc  judge  lib  ...

4.4 编译与安装(domserver + judgehost)

DOMjudge 的编译分两段:configure 生成构建配置,make 分角色构建。先执行 configure 指定安装前缀与运行用户:

cd /root/domjudge-9.0.1
./configure --prefix=/opt/domjudge --with-domjudge-user=domjudge

实测回显(节选,configure 检查项很多,保留关键结论行):

checking for gcc... gcc
checking for gcc option to enable C11 features... none needed
checking domjudge-user... domjudge
checking for php... yes
config.status: creating paths.mk
config.status: creating etc/config.h

⚠️ 排障实录:以 root 执行 configure 时会直接报错拒绝——实测报错原文为 checking domjudge-user... configure: error: installing/running as root is STRONGLY DISCOURAGED, use --with-domjudge-user=root to override.。这是官方的强约束:DOMjudge 拒绝以 root 作为评测运行用户。正确做法是先创建普通用户 domjudge(useradd -m domjudge),再用 --with-domjudge-user=domjudge 显式指定;若强行指定 --with-domjudge-user=root 也可以通过 configure,但评测进程将以 root 运行,官方强烈不建议。

编译两个角色的构建目标(-j$(nproc) 按 CPU 数并行):

make domserver judgehost -j$(nproc)

实测回显(节选,保留构建起点与典型编译命令行):

make[1]: Entering directory '/root/domjudge-9.0.1/etc'
Substituting configure variables in 'apache.conf'.
Substituting configure variables in 'nginx-conf'.
Substituting configure variables in 'nginx-conf-inner'.
Substituting configure variables in 'domjudge-fpm.conf'.
Substituting configure variables in 'domserver-static.php'.
...
gcc -g -O2 -Wall -Wformat -Wformat-security -pedantic -fstack-protector -fPIE -D_FORTIFY_SOURCE=2 -std=c11 -DREVISION="\"release\"" -I/root/domjudge-9.0.1/lib -I/root/domjudge-9.0.1/etc -o evict evict.c /root/domjudge-9.0.1/lib/lib.error.c /root/domjudge-9.0.1/lib/lib.misc.c

安装到 /opt/domjudge(root 权限会自动设置密码文件与目录属主):

make install-domserver install-judgehost

实测回显(节选):

/usr/bin/install -c -d /opt/domjudge/domserver/bin /opt/domjudge/domserver/etc /opt/domjudge/domserver/lib /opt/domjudge/domserver/log /opt/domjudge/domserver/run /opt/domjudge/domserver/webapp/public/images/affiliations /opt/domjudge/domserver/webapp/public/images/countries /opt/domjudge/domserver/webapp/public/images/teams /opt/domjudge/domserver/example_problems /opt/domjudge/domserver/db-dumps
...
========== Judgehost Install Completed ==========

Optionally:
    - Install the create-cgroup service to setup the secure judging restrictions:
        cp judge/create-cgroups.service /etc/systemd/system/
    - Install the judgehost service:
        cp judge/domjudge-judgedaemon@.service /etc/systemd/system/
    - You can enable the judgehost on CPU core 1 with:
        systemctl enable domjudge-judgedaemon@1

验证安装产物结构:

ls /opt/domjudge
ls /opt/domjudge/domserver/bin /opt/domjudge/judgehost/bin

实测回显:

domserver
judgehost
/opt/domjudge/domserver/bin:
configure-domjudge  dj_console  dj_setup_database  export-contest  fix_permissions
force-passwords  import-contest
/opt/domjudge/judgehost/bin:
create_cgroups  dj_judgehost_cleanup  dj_make_chroot
dj_run_chroot  judgedaemon  runguard  runpipe

⚠️ 排障实录:对已安装过的目录重复执行 make install-domserver 会在创建 dj_console 符号链接时报错——实测报错 ln: failed to create symbolic link '/opt/domjudge/domserver/bin/dj_console': File exists,随后 make 以 Error 1 结束。这是安装目标不幂等所致:先删除已存在的链接(rm -f /opt/domjudge/domserver/bin/dj_console)再重跑安装即可成功(实测 EXIT=0)。首次安装不会遇到此问题。

4.5 初始化 domserver 数据库

domserver 的数据库初始化由 dj_setup_database 工具完成:先生成凭据文件(genpass),再建库导入默认数据(install)。MariaDB 全新安装后 root 走 unix_socket 认证,必须加 -s 选项让脚本通过本地 socket 连接:

cd /opt/domjudge/domserver
bin/dj_setup_database -s genpass
bin/dj_setup_database -s install

实测回显:

Database credentials read from '/opt/domjudge/domserver/etc/dbpasswords.secret'.
DOMjudge database and user(s) created.

 [OK] Already at the latest version ("DoctrineMigrations\Version20250907161952")

   > loading App\DataFixtures\DefaultData\ExecutableFixture
   > loading App\DataFixtures\DefaultData\LanguageFixture
   > loading App\DataFixtures\DefaultData\RoleFixture
   > loading App\DataFixtures\DefaultData\TeamCategoryFixture
   > loading App\DataFixtures\DefaultData\TeamFixture
   > loading App\DataFixtures\DefaultData\UserFixture
   > loading App\DataFixtures\ExampleData\JudgehostFixture

初始化完成后,三份关键凭据文件就绪,其中 admin 初始密码存放在 etc/initial_admin_password.secret:

cat etc/dbpasswords.secret
cat etc/initial_admin_password.secret

实测回显(演示环境实测值,生产环境以实际生成为准):

# Randomly generated on host domjudge-lab, Wed Oct 28 05:21:50 UTC 2026
# Format: 'unused:<db_host>:<db_name>:<user>:<password>:<db_port>'
unused:localhost:domjudge:domjudge:oicbAmHBiMGi8YL3:3306
oIvC5NFpXLR9eiiR

💡 说明:dbpasswords.secret 是 domserver 连接 MariaDB 的凭据(格式 unused:<db_host>:<db_name>:<user>:<password>:<db_port>),initial_admin_password.secret 是 Web 管理员 admin 的初始密码。首次登录后应立即在 Web 界面改掉(本文演示统一改为 q1w2e3r4,方法见 4.10 节)。命令行改密工具 force-passwords 与 dj_console domjudge:reset-user-password 的行为与排障见本专栏命令篇。

⚠️ 排障实录:不加 -s 直接执行会报 ERROR 1698 (28000): Access denied for user 'root'@'localhost'——因为 MariaDB 的 root 默认使用 unix_socket 认证插件,而脚本默认尝试以 TCP 方式连接。-s 的含义是「connect via local socket (do not specify port)」(脚本自述),在 Ubuntu 22.04 全新 MariaDB 上是必选项。若 MariaDB root 已设置密码,则用 -u root -p 形式替代。

4.6 配置 nginx 与 PHP-FPM(domserver Web 端)

DOMjudge 官方提供两份生成好的配置:nginx 站点片段与 PHP-FPM 池配置,以符号链接方式接入即可。先把 PHP-FPM 池链接为 8.3 的池配置,再把 nginx 站点配置链接进 sites-enabled:

ln -sf /opt/domjudge/domserver/etc/domjudge-fpm.conf /etc/php/8.3/fpm/pool.d/domjudge.conf
ln -sf /opt/domjudge/domserver/etc/nginx-conf /etc/nginx/sites-enabled/domjudge

两个文件的关键配置(生成自安装模板,默认值即可用):

# /opt/domjudge/domserver/etc/domjudge-fpm.conf(节选)
[domjudge]
user = www-data
group = www-data
listen = /var/run/php-fpm-domjudge.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = static
pm.max_children = 40      ; ~40 per gig of memory(16gb system -> 500)
pm.max_requests = 5000
php_admin_value[memory_limit] = 512M
php_admin_value[upload_max_filesize] = 256M
php_admin_value[post_max_size] = 256M
; This must be strictly larger than the configured max number of source
; files a team can submit.
php_admin_value[max_file_uploads] = 101

逐项说明:listen 是与 nginx 对接的 unix socket(nginx 侧 upstream domjudge 指向它);pm = static + max_children = 40 为固定进程数模型,注释建议按「每 GB 内存 40 个」取值;memory_limit/upload_max_filesize/post_max_size 三个值必须不小于最大测试数据与期望程序输出体积;max_file_uploads = 101 必须大于单次提交允许的源文件数(默认 100)。

重载两个服务使配置生效:

systemctl reload php8.3-fpm
systemctl reload nginx

验证 Web 端已可访问(HTTP 200 且页面标题正常):

curl -s -o /dev/null -w "%{http_code}\n" -L http://localhost/domjudge/
curl -sL http://localhost/domjudge/ | grep -oE "<title>[^<]*</title>" | head -1

实测回显:

200
<title>Scoreboard demo - DOMjudge</title>

💡 说明:nginx 站点配置以 /domjudge/ 为路径前缀(官方模板默认),所有页面、REST API(/domjudge/api/)都在该前缀下。若要部署在独立域名根路径,编辑 /etc/nginx/sites-enabled/domjudge 调整 prefix 与 server_name 即可。反向代理与真实客户端 IP 传递的进阶配置见本专栏配置篇。

4.7 安装 judgehost(评测机)

judgehost 侧需要四件事:评测运行用户、sudo 授权、REST API 凭据、chroot 沙箱。本节完成前三件。

第一步:创建评测运行用户与组(评测程序以此身份运行,无登录能力):

groupadd -f domjudge-run
useradd -d /nonexistent -g domjudge-run -M -s /bin/false domjudge-run

第二步:部署 sudoers 授权片段(安装时已生成,确认在位即可):

cp /opt/domjudge/judgehost/etc/sudoers-domjudge /etc/sudoers.d/domjudge
chmod 440 /etc/sudoers.d/domjudge
head -10 /etc/sudoers.d/domjudge

实测回显(节选):

# Generated from 'sudoers-domjudge.in' on Wed Oct 28 05:25:54 UTC 2026.

# Example sudoers configuration for DOMjudge judgehosts.
# Check the file paths (especially the chroot prefix),
# give it file mode 0440 and place this file into /etc/sudoers.d/
# (most recent sudo versions; otherwise use an #include statement
# in or add its content to the end of /etc/sudoers)

domjudge ALL=(root) NOPASSWD: /opt/domjudge/judgehost/bin/runguard *
domjudge ALL=(root) NOPASSWD: /bin/chown -R domjudge\: /opt/domjudge/judgehost/judgings/*

要点:规则授权 domjudge 用户免密执行 runguard(评测包装器)与 chroot 挂载相关命令;domjudge 是 judgedaemon 的运行用户(由 configure 的 --with-domjudge-user 决定)。

第三步:配置 REST API 凭据。domserver 安装时已自动创建 judgehost 角色用户,凭据写入两侧的 restapi.secret,两侧必须一致:

cat /opt/domjudge/judgehost/etc/restapi.secret

实测回显(演示环境实测值):

# Randomly generated on host domjudge-lab, Wed Oct 28 05:21:50 UTC 2026
# Format: '<ID> <API url> <user> <password>'
default	http://localhost/domjudge/api	judgehost	3ClBDfh1CH6By/zXBMANXIAH/lAJho3e

格式为「ID、API 地址、用户名、密码」四列空白分隔。多机部署时把这份文件复制到每台 judgehost 的 etc/restapi.secret,并把 URL 指向 domserver 的实际地址。先用 curl 验证凭据可用:

curl -s -u judgehost:'3ClBDfh1CH6By/zXBMANXIAH/lAJho3e' http://localhost/domjudge/api/v4/judgehosts

实测回显:

[{"id":"1","hostname":"example-judgehost1","enabled":false,"polltime":null,"hidden":false}]

💡 说明:返回里的 example-judgehost1 是初始化数据自带的示例条目(disabled 状态),不代表本机 judgehost;本机评测机要在 4.9 节 judgedaemon 首次连接后才会自动注册为新条目。

4.8 构建评测 chroot 沙箱

每个提交程序都在 chroot 沙箱内编译运行,沙箱需要包含编译器与运行时库。dj_make_chroot 用 debootstrap 在 /chroot/domjudge 构建一个与宿主同发行版的完整根文件系统,-i 参数指定额外装入的编译器包:

/opt/domjudge/judgehost/bin/dj_make_chroot -i "gcc,g++,openjdk-11-jdk-headless,python3"

实测回显(输出极长,debootstrap 下载解包数百行,保留首尾关键行,实测首尾):

I: Retrieving InRelease
I: Retrieving Packages
I: Validating Packages
I: Resolving dependencies of required packages...
I: Resolving dependencies of base packages...
...
Replacing debian:TrustAsia_TLS_ECC_Root_CA.pem
Replacing debian:TrustAsia_TLS_RSA_Root_CA.pem
...
Reading package lists...
Building dependency tree...
Reading state information...
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
Done building chroot in /chroot/domjudge

验证沙箱就位并可在其中执行命令(dj_run_chroot 不带命令时进入交互 shell,带命令则直接执行):

ls /chroot/domjudge/ | head -8
/opt/domjudge/judgehost/bin/dj_run_chroot gcc --version | head -1

实测回显:

bin   dev  home  lib    lib64  media  opt  root  run
srv   tmp  usr   var
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0

💡 技术拓展:评测程序的安全隔离手段不止 chroot 一种,主流方案对比——

隔离方案原理取舍
chroot(DOMjudge 默认)切换根文件系统 + runguard 限权 + cgroups 资源限制依赖宿主 root,启动开销小,判题计时精确
Namespace 隔离进程/挂载/网络多重隔离隔离更强,但资源与时间计量精度受共享内核影响
虚拟机沙箱内核级隔离隔离最强,启动秒级延迟,判题吞吐低

DOMjudge 9.0 采用「chroot + runguard + cgroups v2」组合,cgroups 还承担网络封锁与多核绑定的职责(judgedaemon -n 参数与 cgroup 一一对应),这也是官方要求内核 ≥ 5.19 的原因。

4.9 systemd 托管(create-cgroups + judgedaemon)

先安装两个官方单元文件:create-cgroups.service(开机创建评测 cgroup)与 domjudge-judgedaemon@.service(模板单元,按核号实例化):

cp /root/domjudge-9.0.1/judge/create-cgroups.service /etc/systemd/system/
cp /root/domjudge-9.0.1/judge/domjudge-judgedaemon@.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now create-cgroups

实测回显:

Synchronizing state of create-cgroups.service with SysV service script with /usr/lib/systemd/systemd-sysv-install.
Executing: /usr/lib/systemd/systemd-sysv-install enable create-cgroups
Created symlink /etc/systemd/system/multi-user.target.wants/create-cgroups.service → /etc/systemd/system/create-cgroups.service.

查看模板单元内容,理解实例化机制:

cat /etc/systemd/system/domjudge-judgedaemon@.service

实测回显(关键段):

[Unit]
Description="DOMjudge judgedaemon bound to CPU #%i"
PartOf=domjudge-judgehost.target

Requires=create-cgroups.service
After=create-cgroups.service
After=network.target

[Service]
Type=simple

ExecStart=/opt/domjudge/judgehost/bin/judgedaemon -n %i
User=domjudge
KillSignal=SIGTERM
TimeoutStopSec=180
FinalKillSignal=SIGKILL

Restart=always
RestartSec=3
PrivateTmp=yes

逐项说明:%i 是实例化占位符(domjudge-judgedaemon@1 实例中取值 1),传给 judgedaemon 的 -n 参数用于绑定 CPU 核;User=domjudge 是 configure 时指定的运行用户;Requires=create-cgroups.service 保证启动前 cgroup 已创建;Restart=always + RestartSec=3 提供故障自动拉起。

启动绑核 1 的评测机实例:

systemctl start domjudge-judgedaemon@1
systemctl is-active domjudge-judgedaemon@1

实测回显:

active

⚠️ 排障实录:若只创建了 domjudge-run 用户就启动 -n 1 实例,服务会陷入重启循环,journalctl -u domjudge-judgedaemon@1 实测报错:error: runuser domjudge-run-1 does not exist.。原因是 judgedaemon 以 -n <id> 绑核运行时,要求存在带核号后缀的运行用户 domjudge-run-<id>(官方手册:useradd -d /nonexistent -g domjudge-run -M -s /bin/false domjudge-run-2)。补建对应用户后重启服务即可:

useradd -d /nonexistent -g domjudge-run -M -s /bin/false domjudge-run-1
systemctl restart domjudge-judgedaemon@1

首次连接 domserver 后评测机自动注册。查看启动日志确认 API 轮询建立:

journalctl -u domjudge-judgedaemon@1 --no-pager -n 8 | tail -8

实测回显:

Oct 28 06:23:36 domjudge-lab judgedaemon[49781]: No submissions in queue (for endpoint default), waiting...
Oct 28 06:23:36 domjudge-lab judgedaemon[49781]: No submissions in queue (for endpoint default), waiting...
Oct 28 06:23:36 domjudge-lab judgedaemon[49781]:
Oct 28 06:23:36 domjudge-lab judgedaemon[49781]: API request GET judgehosts
Oct 28 06:23:36 domjudge-lab judgedaemon[49781]:
Oct 28 06:23:37 domjudge-lab judgedaemon[49781]: API request POST judgehosts/fetch-work
Oct 28 06:23:37 domjudge-lab judgedaemon[49781]:

再从 domserver 侧确认注册结果(新增 enabled=true 的本机条目):

curl -s -u judgehost:'3ClBDfh1CH6By/zXBMANXIAH/lAJho3e' http://localhost/domjudge/api/v4/judgehosts | python3 -m json.tool

实测回显:

[
    {
        "id": "2",
        "hostname": "domjudge-lab-1",
        "enabled": true,
        "polltime": "1793168629.866400000",
        "hidden": false
    }
]

多核服务器可再启动更多实例(每个实例一个核号与对应用户),例如:

useradd -d /nonexistent -g domjudge-run -M -s /bin/false domjudge-run-2
systemctl start domjudge-judgedaemon@2

4.10 端到端验证:从 Web 登录到自动判题

安装是否成功,最终以「登录 → 建赛 → 提交 → 判题」的完整链路验证。

登录验证:浏览器打开 http://<服务器IP>/domjudge/ 进入公共页,点击右上角登录,用户名 admin,初始密码取自 etc/initial_admin_password.secret(本文实测环境为 oIvC5NFpXLR9eiiR)。登录后立即在「My profile」修改为统一演示口令 q1w2e3r4。命令行方式也可用官方工具改密(需以 root 在 domserver 上执行):

cd /opt/domjudge/domserver
bin/dj_console domjudge:reset-user-password admin q1w2e3r4

实测回显:

 [OK] New password for admin is q1w2e3r4

创建比赛与导入题目:安装包自带示例比赛数据(示例比赛 + hello/fltcmp/boolfind 三道题 + 全套判题样例)。若 dj_setup_database install 时已随默认数据一并导入(命令篇有逐条验证),可直接使用;从零导入的方法与 import-contest 用法见命令篇。

提交与判题验证:以团队身份通过 REST API 提交示例题 hello 的 AC 解法(团队用户 demo 的口令同样设为 q1w2e3r4):

curl -s -u demo:q1w2e3r4 -X POST http://localhost/domjudge/api/v4/contests/demo/submissions \
  -F "problem_id=hello" -F "language=c" \
  -F "code[]=@/opt/domjudge/domserver/example_problems/hello/submissions/accepted/test-hello.c"

实测回显:

{"language_id":"c","time":"2026-10-28T06:26:47.404+00:00","contest_time":"0:56:17.404","team_id":"exteam","problem_id":"hello","files":[],"submitid":1,"id":"1"}

等待判题机拉取(judgedaemon 日志出现 Adding judging run result for jt1 successful. 即完成),查询判题结果:

curl -s -u admin:q1w2e3r4 "http://localhost/domjudge/api/v4/contests/demo/judgements?limit=5" | python3 -m json.tool | head -16

实测回显:

[
    {
        "start_time": "2026-10-28T06:26:51.595+00:00",
        "start_contest_time": "0:56:21.595",
        "end_time": "2026-10-28T06:26:53.084+00:00",
        "end_contest_time": "0:56:23.084",
        "max_run_time": 0.001,
        "submission_id": "1",
        "id": "1",
        "valid": true,
        "judgement_type_id": "AC"
    }
]

判题在约 1.5 秒内完成(编译 + 运行 + 比对),结果 AC,说明「Web 提交 → judgedaemon 拉取 → chroot 编译运行 → 结果回写」全链路通畅。至此方案 A 安装完毕。

五、离线安装方案 B(tarball 离线编译)

5.1 方案说明

方案 B 面向无外网(或弱外网)的赛事内网服务器:依赖包与源码 tarball 全部由有网机器预先取得并转入内网,服务器在离线状态下完成与方案 A 完全一致的编译安装流程。与方案 A 的差异只在「依赖与源码的获取渠道」,编译、数据库、Web、评测机、沙箱各步骤完全相同,本章只展开有差异的部分。

5.2 离线依赖准备

在有网机器上用 apt-get download(或 apt-get install --download-only)拉取方案 A 4.2 节的全部依赖包及其依赖闭包,连同 ppa:ondrej/php 的 PHP 8.3 系列一起转入内网:

mkdir -p debs && cd debs
apt-get download build-essential make gcc g++ autoconf automake \
  composer php8.3-cli php8.3-mysql php8.3-zip php8.3-gd php8.3-curl \
  php8.3-intl php8.3-mbstring php8.3-xml php8.3-fpm php8.3-ds php8.3-bcmath \
  nginx mariadb-server acl sudo debootstrap lsof procps

预期结果:目录内生成全部 .deb 文件(依赖闭包建议配合 apt-cache depends --recurse 核对,或直接在一台同版本机器上用 apt-get install --download-only -y 一次拉全)。将 debs/ 目录与官方 tarball(方案 A 4.3 节同一 URL 的 domjudge-9.0.1.tar.gz)一并拷贝到内网服务器。

⚠️ 注意:ppa:ondrej/php 的包在离线机上安装时可能提示依赖缺失(如 php8.3-common 的版本锚定),务必在有网机器上以完全相同的 Ubuntu 版本执行下载,并用 apt-get install -y --no-install-recommends ./debs/*.deb 在内网机本地安装。

5.3 离线编译安装与初始化

内网机安装本地 deb 并解压源码:

apt-get install -y /path/to/debs/*.deb
tar xzf /path/to/domjudge-9.0.1.tar.gz -C /root

之后的步骤与方案 A 完全一致(configure 参数、make 目标、dj_setup_database、nginx/fpm 链接、judgedaemon 单元均相同),唯二注意点:

  • chroot 构建的 debootstrap 需要基础包源:dj_make_chroot 会在线拉取 Ubuntu 基础包。内网机需配置可访问的 Ubuntu 软件源(--mirror 参数指定内网源地址),例如 /opt/domjudge/judgehost/bin/dj_make_chroot -i "gcc,g++,openjdk-11-jdk-headless,python3" --mirror http://<内网源>/ubuntu;或在有网机器上构建好 /chroot/domjudge 后整目录打包拷贝过来(实测同架构同发行版可直接使用)。
  • 版本校验:内网转入的 tarball 建议同时携带官方站的 sha256 校验值核对,防止传输损坏。

5.4 验证

离线安装的验证方法与方案 A 4.10 节完全一致:Web 登录、judgehost 注册、示例提交 AC 判题三项全部通过即视为安装成功。本文方案 A 的全部实测回显对方案 B 同样成立(两方案产物与流程相同)。

六、方案对比与选择建议

维度方案 A:在线 tarball 编译方案 B:离线 tarball 编译
版本官方 9.0.1(与官网一致)同左(tarball 转入)
是否需联网编译期需联网下载依赖与源码编译期完全离线
依赖来源Ubuntu 源 + ppa:ondrej/php有网机预下载 deb 转入
chroot 构建debootstrap 在线拉取需内网源或异地构建拷入
安装方式源码编译(configure/make)源码编译(同左)
systemd 服务名create-cgroups + domjudge-judgedaemon@N同左
优缺点步骤最少、版本最新;依赖外网稳定内网可控;依赖准备环节易踩版本锚定坑

选择建议:可联网服务器一律选方案 A(官方主线、步骤最少);赛场内网、保密内网或外网不稳定的部署场景选方案 B,并提前在有网环境完整演练一遍依赖下载(特别是 PHP 8.3 PPA 的依赖闭包)。两方案产物一致,后续运维、配置、升级方法通用。

七、配置说明

安装完成后,日常会打交道的配置文件族集中在 /opt/domjudge 与 /etc 两处。本节给出生效位置与关键项速查,逐项参数详解见本专栏配置篇。

文件作用关键内容
/opt/domjudge/domserver/etc/dbpasswords.secretdomserver 连 MariaDB 凭据主机/库名/用户/密码/端口
/opt/domjudge/domserver/etc/initial_admin_password.secretadmin 初始密码首登后立即改密
/opt/domjudge/domserver/etc/domserver-static.php安装期生成的静态定义版本号、安装路径、BASEURL
/opt/domjudge/domserver/etc/nginx-conf(+inner)nginx 站点片段/domjudge/ 前缀、API 路由、fpm socket
/opt/domjudge/domserver/etc/domjudge-fpm.confPHP-FPM 池memory_limit 512M、上传 256M
/opt/domjudge/judgehost/etc/restapi.secret评测机对接 domserver 凭据ID/URL/用户/密码 四列
/opt/domjudge/judgehost/etc/judgehost-config.php评测机行为配置syslog、可写临时目录、HTTP 退避参数
/opt/domjudge/judgehost/etc/sudoers-domjudge评测机 sudo 规则runguard、chroot 挂载免密
/chroot/domjudge评测沙箱根文件系统编译器与运行时
/etc/systemd/system/domjudge-judgedaemon@.service评测机 systemd 模板单元-n %i 绑核、User=domjudge

Web 层业务参数(判题内存上限、罚时、榜单显示等)不在文件中,存于数据库 configuration 表,通过 admin 界面 Settings 修改——这部分逐项验证与修改方法见配置篇。

配置修改后的通用验证方法:改 nginx/fpm 配置后 systemctl reload nginx 与 systemctl reload php8.3-fpm 并以 curl -o /dev/null -w "%{http_code}" http://localhost/domjudge/ 确认 200;改 judgedaemon 相关配置后 systemctl restart domjudge-judgedaemon@1 并确认 systemctl is-active 返回 active。

八、常见问题(FAQ)

Q1:configure 报「installing/running as root is STRONGLY DISCOURAGED」怎么办? 先创建普通用户(useradd -m domjudge)并加 --with-domjudge-user=domjudge 重新 configure。这是官方硬约束:评测运行用户不能是 root(详见 4.4 节排障实录)。

Q2:Ubuntu 22.04 源里的 PHP 8.1 能直接用吗? 不能。9.0 系列要求 PHP ≥ 8.2.0,必须经 ppa:ondrej/php 安装 php8.3 全家桶(含官方点名的 ds、bcmath 扩展),否则 domserver 运行异常(详见 4.2 节排障实录)。

Q3:dj_setup_database 报 ERROR 1698 Access denied for root? MariaDB root 默认 unix_socket 认证,脚本默认 TCP 连接。加 -s 选项走本地 socket 即可;若 root 设有密码则用 -u root -p(详见 4.5 节排障实录)。

Q4:judgedaemon 一直 restarting,日志报 runuser domjudge-run-1 does not exist? -n <id> 绑核实例要求对应用户 domjudge-run-<id>。逐个补建(每核一个用户)后 systemctl restart domjudge-judgedaemon@<id>(详见 4.9 节排障实录)。

Q5:重新执行 dj_setup_database genpass/install 后 Web 全站 500? genpass 会重写 dbpasswords.secret/restapi.secret/initial_admin_password.secret 为新随机值,但已存在的数据库用户与 Web 用户密码不会自动跟随,导致 domserver 连库、评测机连 API 全部失配。修复顺序:dj_setup_database -s update-password(把数据库用户密码同步为 secret 新值)→ force-passwords(把 judgehost API 用户与 admin 重置为 secret/初始密码文件的值)→ 用 dj_console domjudge:reset-user-password 恢复自定义口令。本文实测中该链路完整恢复(详见 4.5 节提示与命令篇)。

Q6:直连 domjudge.org 下载极慢或超时? 实测直连约 100–170KB/s 且中途截断,代理(http://192.168.1.62:7897)约 3.8MB/s 稳定完整。按「官方源 → 国内源 → 代理」降级链选择,tarball 也可从有网机器转入(离线场景见方案 B)。

Q7:judgehost 提交后一直 pending 不判题? 依次检查:systemctl is-active domjudge-judgedaemon@1 是否 active;judgedaemon 日志有无 API 连接错误(restapi.secret 是否与 domserver 侧一致);Web 端 admin → Judgehosts 中本机是否 enabled;chroot 是否构建成功(ls /chroot/domjudge)。

Q8:如何加第二台评测机? 新机器上只装 judgehost 角色(4.4 节的 judgehost 构建目标 + 4.7/4.8/4.9 节),把 domserver 的 restapi.secret 复制过去并把 URL 指向 domserver 地址,启动 judgedaemon 后自动注册。判题机扩容是增量操作,无需动 domserver。

九、卸载与清理

卸载按「评测机 → 数据库 → 安装目录 → Web 配置」顺序回退,每步可独立执行。

# 1. 停止并移除评测机与 cgroup 服务
systemctl disable --now domjudge-judgedaemon@1
systemctl disable --now create-cgroups
rm -f /etc/systemd/system/domjudge-judgedaemon@.service /etc/systemd/system/create-cgroups.service
systemctl daemon-reload

# 2. 删除数据库与数据库用户(不可恢复,先备份!)
/opt/domjudge/domserver/bin/dj_setup_database -s uninstall

uninstall 的官方自述为「remove database users and database, INCLUDING ALL DATA!」,执行前务必用 mysqldump --max_allowed_packet=1G --single-transaction domjudge > domjudge-backup.sql 备份(实测提醒:不加 --max_allowed_packet 会在导出大测试用例表时中断,详见命令篇排障实录)。

# 3. 移除安装目录与沙箱
rm -rf /opt/domjudge /chroot/domjudge

# 4. 移除 Web 侧配置并还原服务
rm -f /etc/nginx/sites-enabled/domjudge /etc/php/8.3/fpm/pool.d/domjudge.conf
rm -f /etc/sudoers.d/domjudge
systemctl reload php8.3-fpm && systemctl reload nginx

验证清理彻底:ls /opt/domjudge /chroot/domjudge 应报 No such file or directory;mysql -e "show databases" 中无 domjudge 库;curl -o /dev/null -w "%{http_code}" http://localhost/domjudge/ 不再返回 200。

十、参考链接