本文由 GPT 5.5 模型翻译。
DomJudge 部署
- 按照官方手册正常安装
- 从
/etc/nginx/sites-enabled/default删除默认 nginx 页面 - 修改
/opt/domjudge/domserver/etc/domjudge-fpm.conf中的 FPM 上传参数 - 将以下内容添加到
/etc/mysql/my.cnf
[mysqld]
max_connections=1000
innodb_log_file_size=10GB
max_allowed_packet=2GB
slow_query_log=1
slow_query_log_file=/var/log/mysql_slow.log
net_read_timeout = 7200
net_write_timeout = 7200
[mysqldump]
max_allowed_packet = 1G
- 安装 redis,并根据这篇博客文章将 session 切换到 redis
JudgeHost 部署
使用以下脚本生成 Docker Compose 文件。
JudgeHost Docker Compose 生成脚本
#!/usr/bin/env bash
set -euo pipefail
N="${1:-20}"
cat <<'YAML'
services:
YAML
for i in $(seq 1 "$N"); do
if [[ "$i" -eq 1 ]]; then
cat <<YAML
judgehost-$i:
image: docker.1ms.run/domjudge/judgehost:9.0.0
hostname: judgedaemon-$i
restart: unless-stopped
privileged: true
environment: &judgehost_env
DOMSERVER_BASEURL: "http://<在此填写 URL>/"
JUDGEDAEMON_PASSWORD: "<在此填写密码>"
DAEMON_ID: "$i"
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
networks:
- domjudge-net
YAML
else
cat <<YAML
judgehost-$i:
image: docker.1ms.run/domjudge/judgehost:9.0.0
hostname: judgedaemon-$i
restart: unless-stopped
privileged: true
environment:
<<: *judgehost_env
DAEMON_ID: "$i"
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
networks:
- domjudge-net
YAML
fi
done
cat <<'YAML'
networks:
domjudge-net:
external: true
name: domjudge-judgehosts_default
YAML
比赛期间最好不要修改 judgehost,否则可能会丢失判题任务。
Natsume 部署
仅适用于高于 0.1.2 的 Natsume 版本
- 使用以下命令生成 CA 证书
openssl ecparam -name prime256v1 -genkey -noout -out ca.key
openssl req -new -x509 \
-key ca.key \
-sha256 \
-days 365 \
-out ca.cert \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign" \
-addext "subjectKeyIdentifier=hash"
openssl pkcs8 -topk8 -inform PEM -in ca.key -outform PEM -out ca_pkcs8.key -nocrypt
- 使用这个脚本为 caddy 反向代理生成 TLS 证书
- 使用 Natsume 数据预处理器预处理数据
- 创建队伍分类(Participants、Girls 等)
- 导入由 Natsume 数据预处理器生成的数据:先导入
organizations.json,再导入teams.json,最后导入accounts.yaml - 提前安装 parallel-ssh
监控与性能调优
- 根据这篇博客文章构建带 Brotli 的 nginx
- 安装 Redis,并根据这篇博客文章将 DomJudge session 存储切换到 Redis
- 将 opcache monitor 的
opcache.php放到/opt/domjudge/status/opcache.php - 将以下内容添加到
/etc/nginx/nginx.conf的 http 块中
# DOMjudge 榜单缓存
fastcgi_cache_path /var/cache/nginx/domjudge
levels=1:2
keys_zone=dj_scoreboard:20m
inactive=30s
max_size=200m
use_temp_path=off;
- 使用以下命令创建缓存路径
mkdir -p /var/cache/nginx/domjudge
chown -R www-data:www-data /var/cache/nginx/domjudge
- 编辑
/opt/domjudge/domserver/etc/nginx-conf-inner,它应变成如下配置
nginx-conf-inner 完整配置
# 由 'nginx-conf-inner.in' 于 Fri Apr 3 08:16:46 AM UTC 2026 生成。
# DOMjudge 内部 nginx 配置
# 单独放在一个文件中以避免配置重复
server_name _default_;
# 将最大上传大小设为无限,因为 PHP 对此另有配置
client_max_body_size 0;
# 阻止机器人索引
add_header X-Robots-Tag "none" always;
# nginx 配置中使用的变量
set $domjudgeRoot /opt/domjudge/domserver/webapp/public;
# 在系统根路径运行时,将这里设为 '' 而不是 /domjudge
set $prefix '';
# 禁止直接打开 .php 文件
location ~ ^/+[^/]+\.php$ { return 404; }
location = /team/scoreboard {
limit_except GET HEAD { deny all; }
include fastcgi_params;
fastcgi_pass domjudge;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param SCRIPT_FILENAME $domjudgeRoot/index.php;
fastcgi_param SCRIPT_NAME $prefix/index.php;
fastcgi_param DOCUMENT_ROOT $domjudgeRoot;
fastcgi_param DOCUMENT_URI /public;
fastcgi_param QUERY_STRING "static=true";
fastcgi_param REQUEST_URI /public?static=true;
fastcgi_param SERVER_NAME $host;
fastcgi_param HTTPS $fastcgi_param_https_variable;
fastcgi_param HTTP_X_REQUESTED_WITH "";
fastcgi_cache dj_scoreboard;
fastcgi_cache_key "$scheme://$host/public?static=true";
# TTL = 3 秒
fastcgi_cache_valid 200 3s;
fastcgi_cache_valid any 0;
fastcgi_cache_lock on;
fastcgi_cache_lock_timeout 5s;
fastcgi_cache_lock_age 5s;
fastcgi_cache_use_stale updating;
fastcgi_cache_background_update on;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
fastcgi_hide_header Set-Cookie;
add_header X-Cache-Status $upstream_cache_status always;
}
location / {
root $domjudgeRoot;
location = /monitor/fpm {
access_log off;
include fastcgi_params;
fastcgi_param SCRIPT_NAME /fpm_status;
fastcgi_param DOCUMENT_URI /fpm_status;
fastcgi_param REQUEST_URI /fpm_status;
fastcgi_param SCRIPT_FILENAME /fpm_status;
fastcgi_param SERVER_NAME $host;
fastcgi_param HTTPS $fastcgi_param_https_variable;
fastcgi_pass domjudge;
}
location ^~ /monitor/ {
alias /opt/monitor/;
index index.php index.html;
try_files $uri $uri/ =404;
location ~ \.php$ {
include fastcgi_params;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param SERVER_NAME $host;
fastcgi_param HTTPS $fastcgi_param_https_variable;
fastcgi_pass domjudge;
}
}
try_files $uri @domjudgeFront;
# 单独处理 API 请求,以便拆分日志
location /api/ {
try_files $uri @domjudgeFrontApi;
error_log /var/log/nginx/domjudge-api.log;
access_log /var/log/nginx/domjudge-api.log;
}
}
# 或者可以使用前缀安装
location /domjudge { return 301 /domjudge/; }
# 禁止直接打开 .php 文件
location ~ ^/domjudge/+[^/]+\.php$ { return 404; }
location /domjudge/ {
root $domjudgeRoot;
rewrite ^/domjudge/(.*)$ /$1 break;
try_files $uri @domjudgeFront;
# 单独处理 API 请求,以便拆分日志
location /domjudge/api/ {
rewrite ^/domjudge/(.*)$ /$1 break;
try_files $uri @domjudgeFrontApi;
}
}
location @domjudgeFront {
sub_filter_once off;
sub_filter 'href="/team/scoreboard"' 'href="/team/scoreboard" target="_blank"';
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_pass domjudge;
include fastcgi_params;
fastcgi_param SERVER_NAME $host;
fastcgi_param SCRIPT_FILENAME $domjudgeRoot/index.php;
fastcgi_param SCRIPT_NAME $prefix/index.php;
fastcgi_param REQUEST_URI $prefix$uri?$args;
fastcgi_param DOCUMENT_ROOT $domjudgeRoot;
fastcgi_param HTTPS $fastcgi_param_https_variable;
# 阻止包含前端控制器的 URI。以下请求会返回 404:
# http://domain.tld/app_dev.php/some-path
internal;
}
location @domjudgeFrontApi {
fastcgi_split_path_info ^(.+\.php)(/.*)$;
fastcgi_pass domjudge;
include fastcgi_params;
fastcgi_param SERVER_NAME $host;
fastcgi_param SCRIPT_FILENAME $domjudgeRoot/index.php;
fastcgi_param SCRIPT_NAME $prefix/index.php;
fastcgi_param REQUEST_URI $prefix$uri?$args;
fastcgi_param DOCUMENT_ROOT $domjudgeRoot;
fastcgi_param HTTPS $fastcgi_param_https_variable;
# 阻止包含前端控制器的 URI。以下请求会返回 404:
# http://domain.tld/app_dev.php/some-path
internal;
# API 使用单独日志文件
error_log /var/log/nginx/domjudge-api.log;
access_log /var/log/nginx/domjudge-api.log;
}
# X-Frame-Options 头用于防御所谓的“点击劫持”攻击。
# 如果需要在 HTML frame 或 iframe 中加载 DOMjudge 的一部分(例如公共榜单),
# 只对 DOMjudge 的对应部分禁用此 header。
add_header X-Frame-Options "DENY";
# 下列 header 适用于任何 DOMjudge 安装。
add_header Referrer-Policy "same-origin";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
error_log /var/log/nginx/domjudge.log;
access_log /var/log/nginx/domjudge.log;
- 根据 opcache monitor 调优
php.ini中的 opcache 参数