Repository navigation
Judge Configuration
English | 简体中文
For Ubuntu/systemd deployments generated by the installer. The configuration entry point is /opt/LLMOJ/config/judge.yaml; replace the prefix when customized. This page covers every current judge YAML field and distinguishes backend-supplied limits, host environment variables and generated values.
Unchanged field sources are pinned to 43a88bdd62fbf200889b36932fa921439a293190; the modified capacity generator uses a main URL for the pending revision: config.ts, config-example.yaml, configure-judge.py, executionCapacity.ts, taskQueue.ts, aiRunner.ts.
LIBREOJ_JUDGE_CONFIG_FILE must name the configuration file; missing it is fatal. Required fields have no class defaults. Table examples come from example YAML, not defaults when omitted; generated installer values may differ. Placeholder key/rootfsId values are not runnable. Optional fields accept omission or null; sandbox is required and cpuAffinity is optional. Use documented fields rather than relying on rejection of unknown keys.
| Field | Type/validation/example | Purpose, installed value and limits |
|---|---|---|
serverUrl |
string; example http://api.libreoj.test/
|
Backend origin without /api; connects to namespace /judge, path /api/socket. Local installation uses http://127.0.0.1:2002; remote uses the site HTTPS origin. |
downloadEndpointOverride |
optional string/null; example null
|
Replaces download origin while preserving the original path/query; a path in the override is not a prefix. Local setup uses loopback Nginx; remote normally leaves it unset and must reach signed URLs. |
key |
base64 string, exactly 40 characters | Base64 representation of a 30-byte key, registered separately per judge, not a user password. Keep root:root 0600; never put it in command arguments, screenshots or public logs. |
dataStore |
string path; example /root/judge/data
|
Disk cache of downloaded test files by UUID; installer uses <root>/data/judge/testdata. No total cache-size setting is provided here; monitor disk usage. |
binaryCacheStore |
string path; example /root/judge/cache
|
Compiled binary cache; installer uses <root>/data/judge/cache. Cleared on startup; never point it at problem data, backups or another shared directory. |
binaryCacheMaxSize |
integer bytes; example 536870912 (512 MiB) |
Validator requires an integer; operationally must be at least backend limit.outputSize, or the judge exits after authorization. Not a program memory limit. Installer retains the example. |
taskConsumingThreads |
positive integer; example 2
|
Concurrent backend task/submission consumers, not CPU count; each task can contain many testcases. Values above 3 warn. Generated as min(3,max(1,slots//2)). |
maxConcurrentDownloads |
positive integer; example 10
|
Test-file download queue concurrency; values above 20 warn. Generated as min(4,slots); increasing it does not add execution slots. |
maxConcurrentTasks |
positive integer; example 3
|
Execution queue capacity, effectively the smaller of this and workspace count; shared by compilation, execution and AI validation. Generator requires equality with workspace count; exceeding OS-reported logical CPU count warns. |
taskWorkingDirectories |
nonempty string array; example 3 paths | One unique absolute directory per execution slot; duplicate resolved paths are rejected. Installer uses <root>/data/judge/work/1..N, separate tmpfs mounts, cleared on startup. Never reuse data/cache directories. A missing tmpfs may only warn; that is not a valid deployment. |
rpcTimeout |
positive integer milliseconds; example 20000
|
RPC/authorization timeout; expiry restarts the process and cancels in-flight tasks. Generated value is 30000. |
downloadTimeout |
positive integer milliseconds; example 20000
|
Per-attempt stream timer starts after receiving the response; not a complete DNS/connection/response-header deadline in current code. Installer retains 20000. |
downloadRetry |
positive integer; example 3
|
Total attempts: 3 means first attempt plus up to 2 retries, not 3 extra retries. |
aiRunnerSocket |
optional string; example omitted | Local AI UNIX socket; worker is disabled if this or output directory is absent. All-mode path is <root>/data/judge/ai-runner.sock, root:backend-group 0660; remote uses null. Do not expose as public TCP. |
aiGeneratedDirectory |
optional string; example omitted | Private local AI output directory, <root>/data/ai-generated in all mode; real directory, not symlink, root:backend-group 0750. Shared locally with backend; null remotely. |
aiRunnerGid |
optional integer; example omitted | When AI worker is enabled, separately checked as a positive integer; all mode uses actual libreoj GID. Remote writes 0 with null socket, so disabled; do not use 0 for an enabled worker. |
sandbox.rootfs |
string path; example /opt/rootfs-ng
|
Sandbox root filesystem; installed at <root>/runtime/sandbox-rootfs. Preserve matching recipe, UIDs and mount layout; never recursively chown it. |
sandbox.rootfsId |
64 lowercase hex characters | Required SHA-256 recipe ID; checked exactly against ID file at startup. Mismatch is fatal; example placeholder is not runnable. |
sandbox.rootfsIdFile |
optional string; example omitted | Defaults to <sandbox.rootfs>/etc/libreoj-rootfs-id; immutable images may use an external ID file via an absolute path. |
sandbox.hostname |
optional string/null; example null
|
Sandbox hostname; installer sets libreoj-judge. Not the backend connection address. |
sandbox.immutable |
optional boolean; example omitted | Absent behaves as false; true uses internal /tmp/libreoj paths and avoids creating mount directories inside the image. It does not turn an arbitrary rootfs read-only by itself; use with the matching layout. |
sandbox.resourceMode |
optional default or arbiter; example omitted |
Generated as default, affecting user programs only. Default uses cgroup CPU accounting; arbiter uses process user CPU time and an additional address-space limit. Compiler/checker/interactor stay default. Changes judging semantics, not a speed toggle. |
sandbox.user |
string; example sandbox
|
User resolved inside rootfs to UID/GID; installer uses sandbox. Not the systemd service User; sandbox manager needs its host privileges. |
sandbox.environments |
object of environment variables | Example sets PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin, HOME=/sandbox, LC_ALL=en_US.UTF-8. Use string values; merged with task-specific environment. Does not configure host Node thread pool. |
cpuAffinity.compiler |
optional integer array; example [0,1]
|
Logical CPU IDs allowed for compilation; installer uses selected judge CPU set. |
cpuAffinity.userProgram |
optional integer array; example [1]
|
Logical CPU IDs allowed for user programs; does not pin each slot exclusively to a separate CPU. |
cpuAffinity.interactor |
optional integer array; example [0]
|
Logical CPU IDs allowed for interactors; must fit the cgroup allowed CPU set. |
cpuAffinity.checker |
optional integer array; example [0,1]
|
Logical CPU IDs allowed for checkers; unset affinity adds no per-kind constraint, but systemd/parent cgroup constraints still apply. |
The complete example mapping keys are sandbox.environments.PATH, sandbox.environments.HOME, and sandbox.environments.LC_ALL. sandbox.environments.* is an extensible string mapping, not another fixed field schema; example values appear above.
-
LIBREOJ_JUDGE_CONFIG_FILE: required path; generated service points to<root>/config/judge.yaml. -
LIBREOJ_JUDGE_LOG_LEVEL: defaults toinfo; temporarily useverbose/debugfor diagnosis, keeping logs with signed download URLs private. -
UV_THREADPOOL_SIZE: set before Node starts, an integer from2 × effective execution slots + 2through1024. Node's default 4 is sufficient only for 1 slot; 3 slots need at least 8, and 6 need 14. Native sandbox waits occupy libuv workers; interaction can need two child waits per slot, plus two filesystem workers. Generated value ismax(4, slots*2+2). Restart judge after changes; setting this insidesandbox.environmentsdoes not work. - Generated
NODE_OPTIONS=--max-old-space-size=512controls Node heap, not contestant memory;PATHselects installed Node and host tools. -
HYHOJ_AI_SAMPLE_INPUTS_DIR: private host AI sample-input directory, default/opt/LLMOJ/data/ai-sample-inputs, generated as<root>/data/ai-sample-inputs. Must agree with backend and must not be publicly served.
Ordinary backend consumers use taskConsumingThreads. Compilation, testcase execution and local AI validation share min(maxConcurrentTasks, workspace count) execution slots. Downloads have a separate maxConcurrentDownloads queue. AI runner has a fixed three concurrent generation pipelines and up to eight accepted unfinished requests, including queued work; these are not another three/eight CPU slots and have no YAML tuning keys. Backend AI concurrency is another layer. Raising consumer/AI concurrency alone does not add CPU capacity.
Backend authorization supplies limit.compilerMessage, limit.outputSize, limit.dataDisplay, limit.dataDisplayForSubmitAnswer, and limit.stderrDisplay; these are not judge.yaml fields. Compiler messages over 1 MiB, data display over 1 KiB and stderr display over 10 KiB warn. Binary cache smaller than outputSize is fatal. Problem/language time and memory limits also come from backend.
Initial installation asks for judge instances (execution slots). A slot is a workspace for simultaneous compilation/testcase execution, not another OJ installation or an OS thread. One judge service uses multiple workspaces and sandbox processes. Submission consumers (taskConsumingThreads), execution slots and the Node worker pool (UV_THREADPOOL_SIZE) are separate limits.
Both all mode and remote judges use min(max(0, effective CPU count - 1), effective RAM MiB//256, 511) for the default and maximum. At least two effective CPUs and 256 MiB RAM are needed for one slot; zero capacity rejects local judging. Effective CPU capacity considers process affinity, effective cgroup cpusets and ancestor CPU quotas. Finite quota is floored with a minimum of 1; host nproc alone is insufficient. The generator selects the required IDs from the end of the allowed set, without assuming IDs start at zero or are contiguous.
Effective RAM is the smaller of host RAM and applicable cgroup memory.max. Budget 256 MiB per slot, with no additional reservation for either mode. The maximum is the minimum of effective CPUs minus one, memory slots rounded down and the libuv limit of 511. The seven-slot cap has been removed; the default is automatically capped at the allowed maximum. Explicit positive counts must also fit this maximum and cannot use the reserved CPU. For example, 512 MiB RAM permits at most two slots, provided at least three effective CPUs are available.
| Effective CPUs | Default slots when RAM is sufficient |
|---|---|
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
| 4 | 3 |
| 8 | 7 |
| 16 | 15 |
| 32 | 31 |
With eight effective CPUs (IDs 0–7) and 8 GiB RAM, all mode defaults to CPUs 1–7, seven slots, three consumers, four concurrent downloads, workspaces 1–7 and thread pool 16. Eight CPUs with 4 GiB effective RAM still allow seven slots; with 512 MiB RAM the default and maximum are two slots, using CPUs 6–7 and thread pool 6. High-memory workloads may need a lower explicit count.
Each slot's 512 MiB tmpfs is a maximum filesystem size, not preallocated RAM or a process memory cap. Compilers, programs, page cache, AI and databases share RAM. High-memory workloads may require fewer slots. TasksMax, parent cgroups and CPU quotas still apply; 511 is not a performance or acceptance guarantee. lscpu -e=CPU,CORE,SOCKET,ONLINE distinguishes logical CPUs from physical cores. SMT siblings share a core; affinity/AllowedCPUs do not provide exclusive cores per slot.
The installer and resizer share judge_capacity.py. This new file and policy belong to the pending revision: confirm the deployed version contains them. Older pinned URLs do not represent the new implementation.
The table covers every directive emitted by configure-judge.py and identifies common accounting/limit settings it does not emit. Static templates can differ from resource-aware generated units; review the generated files and higher-priority service drop-ins.
| Directive | Generated value | Meaning/constraints |
|---|---|---|
Unit.Description |
Judge: slots/consumers; workspace/remote target descriptions |
Descriptive text, not a resource limit. |
Unit.After |
local: libreoj-backend.service; remote: network-online.target |
Startup ordering; remote target also follows network-online.target. |
Unit.Requires |
local service: libreoj-backend.service |
No backend dependency on a remote node; stopping local backend affects its dependent judge. |
Unit.Wants |
remote service: network-online.target; remote target: libreoj-judge.service |
Weak dependencies to activate remote networking/judge; not a multi-node scheduling rule. |
Unit.PartOf |
service and mounts: libreoj.target or libreoj-judge.target |
Different all/remote stop/restart membership. Resize stops judge service, not the whole all target. |
Unit.RequiresMountsFor |
all taskWorkingDirectories |
Service depends on configured workspace mounts; must match YAML. |
Unit.Before |
not explicitly generated |
No explicit Before value; RequiresMountsFor and systemd default dependencies supply ordering. |
Service.WorkingDirectory |
<root>/apps/judge |
Node working directory; dependencies there must remain readable. |
Service.Environment |
NODE_ENV=production; PATH=<node-parent>:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
Also generates LIBREOJ_JUDGE_CONFIG_FILE, UV_THREADPOOL_SIZE, NODE_OPTIONS=--max-old-space-size=512, HYHOJ_AI_SAMPLE_INPUTS_DIR; see host environment section. |
Service.ExecStart |
<node> -r @swc-node/register index.mjs |
Judge uses the TypeScript registration entry, not backend dist/main.js. |
Service.Delegate |
cpu memory pids |
Delegates cgroup controllers to native sandbox; do not remove or replace with a sudo prefix. |
Service.DelegateSubgroup |
supervisor |
Requires systemd254+; separates manager from sandbox child cgroups. |
Service.OOMPolicy |
continue |
Does not stop/kill the entire service via OOMPolicy for a sandbox OOM; does not prevent host exhaustion. |
Service.AllowedCPUs |
union of the four cpuAffinity sets |
Logical CPU IDs, constrained by parent cgroup; regenerate and restart. |
Service.TasksMax |
2048 |
Host service task/thread limit, not submission or execution-slot count. |
Service.Restart |
on-failure |
Restarts after failure; bad configuration may repeatedly fail, requiring log inspection. |
Service.RestartSec |
3 |
Wait three seconds before restarting after failure. |
Service.TimeoutStopSec |
30 |
Thirty-second stop timeout is not queue draining and does not guarantee in-flight completion. |
Service.UMask |
0022 |
Default mask for ordinary new judge files; private config and AI paths/socket use explicit stricter permissions. |
Service.LimitCORE |
0 |
Disables core dumps. Other Limit* settings such as LimitNOFILE are not emitted and inherit host/systemd settings. |
Service.StandardOutput |
journal |
Standard output goes to journal; keep diagnostic logs private. |
Service.StandardError |
journal |
Standard error goes to journal. |
Service.User / Service.Group |
not explicitly generated |
System service therefore uses host root by default; sandbox.user is a different inside-sandbox identity. |
Service.CPUAccounting / Service.MemoryAccounting / Service.TasksAccounting |
not explicitly generated |
Inherit systemd manager/parent-slice settings; do not claim generator sets a value. Inspect effective settings with systemctl show. |
Service.MemoryMax / Service.CPUQuota |
not explicitly generated |
Parent cgroup or drop-in can still limit them; per-problem sandbox limits are configured separately. |
Mount.What |
tmpfs |
One independent tmpfs filesystem per workspace. |
Mount.Where |
each taskWorkingDirectories path |
Unit name comes from systemd-escape --path --suffix=mount; do not guess path escaping. |
Mount.Type |
tmpfs |
Not the disk testdata store; do not mount over rootfs to replace storage. |
Mount.Options |
size=512m,mode=0755,nodev,nosuid |
Size is each tmpfs maximum; mode controls mount-root permissions; nodev/nosuid prevent device-node/setuid behavior. |
Install.WantedBy |
service/mounts: mode target; remote target: multi-user.target |
Enable creates target .wants links; remote target needs boot enablement. Resize retains existing target enablement. |
Inspect effective CPUAccounting, MemoryAccounting, and TasksAccounting read-only; resizing does not require independently changing them.
sudo systemctl show libreoj-judge.service -p CPUAccounting -p MemoryAccounting -p TasksAccounting -p LimitCORE -p TasksMaxUse resize-judge.sh instead of copying a long maintenance program. The new script belongs to the pending revision; install the matching update files if an older deployment lacks it. Installer options describe initial setup. Do not force resizing by deleting private YAML/keys, editing installation metadata or rerunning installation with a different --judge-slots value.
The script requires an installed libreoj-judge.service, standard work/1..N workspaces and managed service/mount units. A web-only node has no local judge to resize; see remote judges. All mode retains its local AI worker, while remote mode uses libreoj-judge.target. Default --mode auto detects the installed service; optional --mode all or --mode remote validates that selection rather than converting node roles.
The wrapper calls resize-judge.py beside it and forwards arguments without downloading a remote script. Direct main-unit MemoryMax / CPUQuota, any related drop-in, or a service bound to another prefix is rejected for manual review.
sudo bash /opt/LLMOJ/deploy/resize-judge.sh --prefix /opt/LLMOJ --plan
# Example only: the target must fit actual CPU and RAM capacity.
sudo bash /opt/LLMOJ/deploy/resize-judge.sh --prefix /opt/LLMOJ --slots 6 --plan--plan writes no configuration and stops no services. Review old/new slots, CPU set, RAM budget and worker pool. Change --prefix for a custom installation. Drop-ins or nonstandard layouts requiring manual review are rejected rather than deleted to bypass validation.
Schedule maintenance, prevent new submissions, rejudges, test runs and AI jobs, then wait for queued/running work from every user. Idle CPUs or your own AI list do not prove draining. --drained is operator confirmation; it does not automatically drain queues.
Default interactive mode displays capacity and the draining requirement in Chinese, then asks only for instance count. Submitting the number (or Enter for the default) confirms draining and immediately applies maintenance, with no second confirmation. Press Ctrl+C if work has not drained:
sudo bash /opt/LLMOJ/deploy/resize-judge.sh --prefix /opt/LLMOJAfter draining, a noninteractive example is:
sudo bash /opt/LLMOJ/deploy/resize-judge.sh --prefix /opt/LLMOJ --slots 6 --apply --drainedThe script backs up private judge YAML, managed units and state under <prefix>/backups/judge-resize/<UTC timestamp>/, prints that private directory, then stops only the judge. It updates slots, workspaces, CPU affinity, consumer/download concurrency, worker pool, mounts and units. It preserves node key, server URL, rootfs and AI configuration. It validates units and runs a real sandbox probe. A previously running judge is restarted and its effective CPU/worker-pool settings checked; a previously stopped judge stays stopped. Verification failure triggers an attempted restore; if restoration also fails, the judge stays stopped and the backup path is reported for manual rollback. Keep the backup private because its YAML contains the node key.
Confirm service health and an online judge on the website, then submit ordinary and interaction/communication problems in required languages. With local AI, also check a small AI data job. A successful command is not acceptance of every workflow.
sudo systemctl is-active libreoj-judge.service
sudo systemctl show libreoj-judge.service -p AllowedCPUs -p EffectiveCPUs -p TasksMaxTo roll back, block new work and drain again, replacing the path with the actual private backup printed by the script:
sudo bash /opt/LLMOJ/deploy/resize-judge.sh --prefix /opt/LLMOJ --rollback /path/to/private-backup --drainedRollback restores saved configuration/units and recorded service/mount state, not problem/database content. It does not undo completed submissions. Verify node connection and actual submissions again after rollback.
This documents the pending revision and isolated regression scope, not a live-server resize or fresh Ubuntu 26.04 acceptance run. Related: Remote judge · Distributed judging.
简体中文
- LLMOJ Wiki
- AI 运行环境变量与固定限制
- AI 配置、权限与执行流程
- 后端配置完整参考
- 团队、题解、作废、Hack 与自测升级
- 配置总览与生效规则
- 比赛赛制
- 分布式评测与网页分离
- Docker 源
- 环境变量、构建与维护工具
- 前端与 bootstrap 配置
- HTTPS 与反向代理
- Hack
- 安装与端口配置
- 评测端完整配置与 CPU 扩缩容
- 基础服务与安装后运维配置
- 发布源码、版本与 GitHub Wiki
- 新用户注册审核
- 独立评测机安装
- Self Eval(NOI / OI 比赛预测试)
- 题解
- 评测记录 Fake
- 评测记录预览
- 团队
- 两步验证
- 保留配置与数据的一键升级
English
- LLMOJ Wiki
- AI configuration, permissions and execution
- AI runtime environment and fixed limits
- Complete backend configuration reference
- Teams, solutions, moderation, Hack and self-evaluation upgrade
- Configuration overview and activation
- Contest rules
- Distributed judging and web/judge separation
- Docker sources
- Environment, builds, and maintenance tools
- Frontend and bootstrap configuration
- HTTPS and reverse proxies
- Hacks
- Installation and ports
- Complete judge configuration and CPU resizing
- Infrastructure and post-install operations
- Source, release and GitHub Wiki publishing
- Registration approval
- Independent judge installation
- Self Eval for NOI / OI contests
- Solutions
- Fake submission moderation
- Submission previews
- Teams
- Two-factor sign-in
- Upgrading while preserving configuration and data