Deploying DOMjudge
DOMjudge is a sophisticated, open-source automated judge system designed for running programming contests. Originally developed for the ACM International Collegiate Programming Contest (ICPC), DOMjudge has become the standard platform for competitive programming events worldwide, from university competitions to international championships. The system automatically compiles, runs, and judges programming submissions against predefined test cases, providing instant feedback to contestants and real-time leaderboards for spectators.
What sets DOMjudge apart is its robustness and security. Each submission runs in an isolated sandbox environment, preventing malicious code from affecting the system or other submissions. The platform supports multiple programming languages including C, C++, Java, Python, and more, with configurable time and memory limits. Features like balloon tracking, clarification systems, team registration, and scoreboard freezing make it a complete solution for running professional programming competitions.
Why Deploy DOMjudge on Klutch.sh?
Klutch.sh provides an excellent platform for hosting DOMjudge with several key advantages:
- Simple Docker Deployment: Deploy your Dockerfile and Klutch.sh automatically handles containerization and orchestration
- Persistent Storage: Attach volumes for submissions, test data, and database storage with guaranteed durability
- Automatic HTTPS: All deployments come with automatic SSL certificates for secure contest access
- Resource Scalability: Scale CPU and memory resources during peak contest periods
- Database Integration: Run MariaDB/MySQL alongside DOMjudge with persistent storage
- Cost-Effective: Pay only for resources used during contests, scale down between events
- Zero Server Management: Focus on running contests, not managing infrastructure
Prerequisites
Before deploying DOMjudge, ensure you have:
- A Klutch.sh account (sign up at klutch.sh)
- Git installed locally
- Basic understanding of competitive programming and judging systems
- Familiarity with Docker and container concepts
- Problem sets prepared (input/output test cases)
- Understanding of programming contest formats
- MariaDB or MySQL knowledge for database management
Understanding DOMjudge’s Architecture
DOMjudge uses a multi-component architecture designed for security and scalability:
Core Components
Domserver (Web Interface): The central component that provides the web interface for teams, judges, and administrators. Built with Symfony PHP framework, it handles:
- Team registration and authentication
- Problem submission and viewing
- Scoreboard generation and display
- Contest configuration and management
- Clarification requests and responses
- Balloon tracking for solved problems
Judgehost (Judgment Engine): Separate daemon processes that fetch submissions from the queue, compile code, run against test cases, and return verdicts. Key features:
- Isolated sandbox execution using cgroups and namespaces
- Support for multiple programming languages
- Configurable CPU and memory limits
- Parallel judging on multiple hosts
- Automatic failover and load balancing
MariaDB/MySQL Database: Stores all contest data including:
- Team information and credentials
- Problem descriptions and test cases
- Submissions and judgments
- Contest configuration
- Scoreboard data
- Clarifications and balloon status
Submission Queue: Redis-based queue (optional) or database-backed queue manages submission processing, ensuring fairness and preventing race conditions.
Security Model
Sandbox Isolation: Each submission runs in a restricted environment:
- Limited system calls (seccomp filters)
- No network access
- Restricted file system access
- CPU and memory limits enforced
- Time limits with precision timing
- Process limits to prevent fork bombs
User Separation: Different privilege levels:
- Admin: Full system access, contest configuration
- Jury: Judge submissions, answer clarifications, manage balloons
- Team: Submit solutions, request clarifications, view scoreboard
- Public: View scoreboard only (if enabled)
Judgment Flow
- Team submits solution through web interface
- Submission enters judgment queue
- Judgehost picks up submission from queue
- Code is compiled with appropriate compiler
- Compiled program runs against each test case
- Output is compared with expected output
- Verdict is determined (Accepted, Wrong Answer, Time Limit, etc.)
- Result is stored in database and displayed to team
- If accepted, balloon notification is generated
- Scoreboard is updated in real-time
Supported Verdicts
- Accepted (AC): Correct output for all test cases
- Wrong Answer (WA): Incorrect output
- Time Limit Exceeded (TLE): Execution exceeded time limit
- Run-Time Error (RTE): Program crashed or exited abnormally
- Compile Error (CE): Code failed to compile
- No Output (NO): Program produced no output
- Output Limit (OL): Output exceeded size limit
- Validator Crashed: Output validator had an error
Installation and Setup
Step 1: Create the Dockerfile for Domserver
Create a Dockerfile in your project root for the web server:
FROM domjudge/domserver:latest
# Set environment variablesENV CONTAINER_TIMEZONE=UTC \ MYSQL_HOST=mysql \ MYSQL_DATABASE=domjudge \ MYSQL_USER=domjudge \ MYSQL_PASSWORD=domjudge \ MYSQL_ROOT_PASSWORD=rootpassword
# Install additional dependenciesRUN apt-get update && apt-get install -y \ curl \ vim \ zip \ unzip \ && rm -rf /var/lib/apt/lists/*
# Create necessary directoriesRUN mkdir -p /domjudge/submissions \ /domjudge/output \ /domjudge/tmp
# Set proper permissionsRUN chown -R www-data:www-data /domjudge
# Copy custom configurationCOPY domserver-config.php /opt/domjudge/domserver/etc/domserver-config.php
# Expose HTTP portEXPOSE 80
# Health checkHEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \ CMD curl -f http://localhost/api/info || exit 1
# Use default entrypoint from base imageStep 2: Create the Dockerfile for Judgehost
Create Dockerfile.judgehost for the judging daemon:
FROM domjudge/judgehost:latest
# Set environment variablesENV CONTAINER_TIMEZONE=UTC \ DOMSERVER_BASEURL=http://domserver/ \ JUDGEDAEMON_USERNAME=judgehost \ JUDGEDAEMON_PASSWORD=password
# Install additional language supportRUN apt-get update && apt-get install -y \ build-essential \ gcc \ g++ \ python3 \ python3-pip \ openjdk-17-jdk \ kotlin \ golang \ rustc \ && rm -rf /var/lib/apt/lists/*
# Create judge directoriesRUN mkdir -p /judgings
# Set proper permissionsRUN chown -R domjudge:domjudge /judgings
# Health checkHEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD pgrep -f judgedaemon || exit 1
# Use default entrypoint from base imageStep 3: Create Configuration Files
Create domserver-config.php for custom configuration:
<?php/** * DOMjudge configuration overrides */
// Database configuration (can be overridden by environment variables)// These are set via environment variables in the Dockerfile
// Timezonedate_default_timezone_set('UTC');
// Base URL for the DOMjudge installation// Will be auto-detected, but can be overridden// $baseurl = 'https://your-app.klutch.sh';
// Password hashing algorithm// Use 'bcrypt' for better security$password_hash_algorithm = PASSWORD_BCRYPT;
// Enable printing (usually disabled for online contests)$enable_printing = false;
// Penalty time for wrong submissions (in minutes)$penalty_time = 20;
// Scoreboard refresh rate (in seconds)$scoreboard_refresh_rate = 30;
// Allow self-registration$allow_self_registration = false;
// Enable team categories$show_teams_in_categories = true;
// Enable external contest APIs$enable_external_api = false;
// Judging priority weights$judging_priority = [ 'new' => 5, 'rejudging' => 3,];
// File upload size limits$upload_max_size = 10; // MB
// Compile and execute defaults$default_compile_time = 30; // seconds$default_run_time = 5; // seconds$default_memory_limit = 2097152; // KB (2GB)$default_filesize_limit = 4096; // KB
// Logging level$log_level = 'INFO'; // DEBUG, INFO, WARNING, ERROR?>Step 4: Create Database Initialization Script
Create init-db.sh:
#!/bin/bashset -e
# Wait for MySQL to be readyecho "Waiting for MySQL to be ready..."until mysql -h "$MYSQL_HOST" -u root -p"$MYSQL_ROOT_PASSWORD" -e "SELECT 1" >/dev/null 2>&1; do echo "MySQL not ready, waiting..." sleep 2done
echo "MySQL is ready!"
# Create database and user if they don't existmysql -h "$MYSQL_HOST" -u root -p"$MYSQL_ROOT_PASSWORD" <<EOFCREATE DATABASE IF NOT EXISTS ${MYSQL_DATABASE};CREATE USER IF NOT EXISTS '${MYSQL_USER}'@'%' IDENTIFIED BY '${MYSQL_PASSWORD}';GRANT ALL PRIVILEGES ON ${MYSQL_DATABASE}.* TO '${MYSQL_USER}'@'%';FLUSH PRIVILEGES;EOF
echo "Database initialized successfully!"
# Import DOMjudge schema if database is emptyTABLES=$(mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" -e "SHOW TABLES;" | wc -l)if [ "$TABLES" -eq 0 ]; then echo "Importing DOMjudge schema..." mysql -h "$MYSQL_HOST" -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE" < /opt/domjudge/domserver/etc/db-config.sql echo "Schema imported successfully!"fiStep 5: Create Docker Compose for Local Development
Create docker-compose.yml:
version: '3.8'
services: mariadb: image: mariadb:10.11 environment: MYSQL_ROOT_PASSWORD: rootpassword MYSQL_DATABASE: domjudge MYSQL_USER: domjudge MYSQL_PASSWORD: domjudge volumes: - mariadb-data:/var/lib/mysql ports: - "3306:3306" restart: unless-stopped
domserver: build: context: . dockerfile: Dockerfile depends_on: - mariadb ports: - "8080:80" environment: MYSQL_HOST: mariadb MYSQL_DATABASE: domjudge MYSQL_USER: domjudge MYSQL_PASSWORD: domjudge MYSQL_ROOT_PASSWORD: rootpassword volumes: - domserver-submissions:/domjudge/submissions - domserver-output:/domjudge/output restart: unless-stopped
judgehost: build: context: . dockerfile: Dockerfile.judgehost depends_on: - domserver privileged: true environment: DOMSERVER_BASEURL: http://domserver/ JUDGEDAEMON_USERNAME: judgehost JUDGEDAEMON_PASSWORD: password volumes: - judgehost-judgings:/judgings restart: unless-stopped
volumes: mariadb-data: domserver-submissions: domserver-output: judgehost-judgings:Step 6: Initialize Git Repository
git initgit add Dockerfile Dockerfile.judgehost domserver-config.php init-db.sh docker-compose.ymlgit commit -m "Initial DOMjudge deployment configuration"Step 7: Test Locally
Before deploying to Klutch.sh, test locally:
# Build and start containersdocker-compose up -d
# Check logsdocker-compose logs -f domserverdocker-compose logs -f judgehost
# Access DOMjudge at http://localhost:8080# Default admin credentials: admin / password (change immediately)Deploying to Klutch.sh
Step 1: Deploy MariaDB Database
First, deploy a MariaDB instance:
- Navigate to klutch.sh/app
- Click "New Project"
- Select MariaDB or use a custom Dockerfile
- Configure database: - Database name: `domjudge` - Username: `domjudge` - Password: Create a secure password - Root password: Create a secure root password
- Select **TCP** as traffic type
- Set internal port to **3306**
- Add persistent storage with mount path: `/var/lib/mysql` and size: `20GB`
- Note the connection details (hostname like `mariadb-app.klutch.sh:8000`)
Step 2: Push Domserver Repository to GitHub
Create a new repository for the domserver and push:
git remote add origin https://github.com/yourusername/domjudge-klutch.gitgit branch -M mastergit push -u origin masterStep 3: Deploy Domserver to Klutch.sh
- Navigate to klutch.sh/app
- Click "New Project" and select "Import from GitHub"
- Authorize Klutch.sh to access your GitHub repositories
- Select your DOMjudge domserver repository
- Klutch.sh will automatically detect the Dockerfile
Step 4: Configure Domserver Traffic Settings
- In the project settings, select **HTTP** as the traffic type
- Set the internal port to **80**
- Klutch.sh will automatically provision an HTTPS endpoint
Step 5: Add Persistent Storage for Domserver
DOMjudge requires persistent storage for submissions and output:
- In your project settings, navigate to the "Storage" section
- Add a volume with mount path: `/domjudge/submissions` and size: `50GB` (for submissions)
- Add a volume with mount path: `/domjudge/output` and size: `20GB` (for judgment output)
Storage recommendations:
- Small contests (< 100 teams): 20GB submissions, 10GB output
- Medium contests (100-500 teams): 50GB submissions, 20GB output
- Large contests (500+ teams): 100GB+ submissions, 50GB+ output
Step 6: Configure Domserver Environment Variables
Add the following environment variables in Klutch.sh dashboard:
MYSQL_HOST: Your MariaDB hostname (e.g.,mariadb-app.klutch.sh)MYSQL_PORT:8000(external TCP port)MYSQL_DATABASE:domjudgeMYSQL_USER:domjudgeMYSQL_PASSWORD: Your database passwordMYSQL_ROOT_PASSWORD: Your root passwordCONTAINER_TIMEZONE:UTC(or your timezone)
Step 7: Deploy Domserver
- Review your configuration settings
- Click "Deploy" to start the deployment
- Monitor build logs for any errors
- Once deployed, domserver will be available at `your-app.klutch.sh`
Step 8: Deploy Judgehost (Optional, Recommended)
For the judging daemon, create a separate deployment:
- Create a new repository with `Dockerfile.judgehost` renamed to `Dockerfile`
- Push to GitHub and import to Klutch.sh
- Configure environment variables: - `DOMSERVER_BASEURL`: `https://your-app.klutch.sh` - `JUDGEDAEMON_USERNAME`: `judgehost` - `JUDGEDAEMON_PASSWORD`: Create secure password - `CONTAINER_TIMEZONE`: `UTC`
- Add persistent storage with mount path: `/judgings` and size: `20GB`
- Select **HTTP** traffic (for health checks)
- Deploy the judgehost
Note: Judgehost requires privileged mode for sandboxing. If this is not available on Klutch.sh, you may need to run judgehosts on separate infrastructure.
Getting Started with DOMjudge
Initial Setup
After deployment, complete the initial configuration:
-
Access DOMjudge: Navigate to
https://your-app.klutch.sh -
First Login: Use default credentials:
- Username:
admin - Password:
password(or check container logs)
- Username:
-
Change Admin Password:
- Click “admin” in top-right corner
- Select “Change Password”
- Enter a strong password
- Save changes
-
Create Judgehost User:
- Navigate to “Configuration” → “Users”
- Click “Add new user”
- Create user with role “Judgehost”
- Username:
judgehost - Set a secure password (use for
JUDGEDAEMON_PASSWORD)
Creating a Contest
Set up your first programming contest:
- Navigate to "Configuration" → "Contests"
- Click "Add new contest"
- Configure contest settings: - **Name**: "Practice Contest 2024" - **Short Name**: "practice2024" - **Activate Time**: Set to 1 hour before start - **Start Time**: Contest start time - **Freeze Time**: Scoreboard freeze (optional, typically 1 hour before end) - **End Time**: Contest end time - **Unfreeze Time**: When to unfreeze scoreboard (after contest ends) - **Process Balloons**: Enable if using balloon system
- Click "Save" to create contest
Adding Problems
Add problems to your contest:
- Navigate to "Problems"
- Click "Add new problem"
- Configure problem: - **Label**: A, B, C, etc. - **Name**: "Hello World", "Sum Two Numbers" - **Time Limit**: 1 second (typical) - **Memory Limit**: 256 MB (typical)
- Upload problem package (ZIP file with format): - `problem.yaml` - problem metadata - `data/sample/*.in` - sample input - `data/sample/*.ans` - sample output - `data/secret/*.in` - test input - `data/secret/*.ans` - test output
- Or manually create test cases: - Click on problem name - Navigate to "Test cases" - Add input/output pairs - Mark as sample or secret
Example problem.yaml:
name: Hello Worldtime_limit: 1.0memory_limit: 256Registering Teams
Add teams to participate:
- Navigate to "Teams"
- Click "Add new team"
- Configure team: - **Name**: "Team Alpha" - **Category**: Select category (University, High School, etc.) - **Affiliation**: University/Organization name - **Members**: Team member names
- Create team credentials: - Go to "Users" - Click "Add new user" - Username: team name or number - Password: Generate or set password - Role: "Team" - Associated team: Select the team
- Provide credentials to team
Bulk import teams via TSV:
teams 11 TeamName1 University A Category12 TeamName2 University B Category1Creating Team Categories
Organize teams by category:
- Navigate to "Configuration" → "Team Categories"
- Click "Add new category"
- Set category name: "University", "High School", "Industry"
- Set visibility and color
- Save category
Configuring Languages
Enable programming languages:
- Navigate to "Configuration" → "Languages"
- Enable languages: - C (gcc) - C++ (g++) - Java (OpenJDK) - Python 3 - Kotlin - Go - Rust
- Set time/memory multipliers if needed (e.g., Java gets 2x time)
- Test each language with a sample submission
Testing Submissions
Test the judging system:
-
Login as a Team: Use team credentials
-
View Problems: Click “Problems” to see available problems
-
Submit Solution:
- Click on problem name
- Write or paste code
- Select language
- Click “Submit”
Example C++ solution for “Hello World”:
#include <iostream>using namespace std;
int main() { cout << "Hello World!" << endl; return 0;}-
View Submission: Navigate to “Submissions” to see status
-
Check Verdict: Should show “Correct” if output matches
Managing Clarifications
Handle team questions during contest:
-
Teams Request Clarifications:
- Team clicks “Request Clarification”
- Selects problem (or general)
- Enters question
- Submits request
-
Jury Responds:
- Navigate to “Clarifications”
- View unanswered requests
- Type response
- Choose visibility (specific team or all teams)
- Submit answer
Monitoring Contest
During the contest, jury/admin can monitor:
-
Scoreboard: Real-time standings
- Navigate to “Scoreboard”
- View team rankings
- See problem status (attempts, first solve)
- Monitor freeze status
-
Submissions: All team submissions
- Navigate to “Submissions”
- Filter by problem, team, verdict
- Rejudge if needed
-
Balloon Tracking:
- Navigate to “Balloons”
- See which teams solved problems
- Mark balloons as delivered
- Track first solves
-
Judging Queue:
- Navigate to “Judging Runs”
- Monitor judgehost activity
- Check for stuck judgments
Production Best Practices
Security Hardening
- Change Default Passwords: Immediately change all default passwords:
# Generate strong passwordsopenssl rand -base64 32Update:
- Admin password
- Judgehost password
- Database passwords
- Restrict Admin Access: Limit admin interface to specific IPs:
// In domserver-config.php$restrict_admin_ip = ['192.168.1.0/24', '10.0.0.0/8'];-
HTTPS Only: Ensure all connections use HTTPS (automatic with Klutch.sh)
-
Disable Self-Registration:
$allow_self_registration = false;- Rate Limiting: Prevent submission flooding:
$submission_rate_limit = 60; // Max submissions per hour per team- Secure Database Connection: Use strong database passwords and restrict access:
# In MariaDBREVOKE ALL PRIVILEGES ON *.* FROM 'domjudge'@'%';GRANT ALL PRIVILEGES ON domjudge.* TO 'domjudge'@'%';Performance Optimization
- Database Tuning: Optimize MariaDB configuration:
# my.cnf[mysqld]innodb_buffer_pool_size = 4Ginnodb_log_file_size = 512Mmax_connections = 200query_cache_size = 256Mtmp_table_size = 256Mmax_heap_table_size = 256M- Multiple Judgehosts: Deploy multiple judgehost instances for parallel judging:
# Scale judgehosts based on contest size# Small: 1-2 judgehosts# Medium: 3-5 judgehosts# Large: 5-10+ judgehosts- Scoreboard Caching: Enable caching for scoreboard:
$scoreboard_cache_ttl = 30; // Cache for 30 seconds- Problem Package Optimization: Minimize test case sizes:
- Compress test data
- Remove unnecessary files
- Optimize validator performance
Backup Strategy
Implement comprehensive backups:
- Database Backups: Schedule regular MySQL dumps:
#!/bin/bashBACKUP_DIR="/backups/domjudge_$(date +%Y%m%d_%H%M%S)"mkdir -p $BACKUP_DIR
# Backup databasemysqldump -h $MYSQL_HOST -P 8000 -u $MYSQL_USER -p$MYSQL_PASSWORD \ $MYSQL_DATABASE > $BACKUP_DIR/domjudge.sql
# Compress backupgzip $BACKUP_DIR/domjudge.sql
# Keep only last 30 daysfind /backups -name "domjudge_*.sql.gz" -mtime +30 -delete- Submissions Backup: Archive all submissions:
# Backup submissions directorytar -czf $BACKUP_DIR/submissions.tar.gz /domjudge/submissions- Configuration Backup: Save configuration files:
# Backup configcp /opt/domjudge/domserver/etc/domserver-config.php $BACKUP_DIR/- Automated Backup Schedule:
# Add to crontab0 2 * * * /usr/local/bin/backup-domjudge.shMonitoring and Logging
- Enable Detailed Logging:
// In domserver-config.php$log_level = 'DEBUG';$enable_audit_log = true;- Monitor Judgehost Activity:
# Check judgehost logstail -f /var/log/domjudge/judgehost.log
# Monitor judgment queuewatch -n 5 'mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASSWORD \ -e "SELECT COUNT(*) FROM judging WHERE result IS NULL" domjudge'- Database Monitoring:
-- Active connectionsSHOW PROCESSLIST;
-- Slow queriesSELECT * FROM information_schema.PROCESSLISTWHERE TIME > 5 ORDER BY TIME DESC;
-- Database sizeSELECT table_schema AS "Database", ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS "Size (MB)"FROM information_schema.TABLESGROUP BY table_schema;- Health Check Script:
#!/bin/bashDOMSERVER_URL="https://your-app.klutch.sh"
# Check domserverif ! curl -f -s -o /dev/null "$DOMSERVER_URL/api/info"; then echo "ERROR: Domserver not responding" exit 1fi
# Check judgehostsACTIVE_JUDGEHOSTS=$(curl -s "$DOMSERVER_URL/api/judgehosts" | jq '.[] | select(.enabled==true)' | jq -s length)if [ "$ACTIVE_JUDGEHOSTS" -eq 0 ]; then echo "WARNING: No active judgehosts"fi
echo "OK: DOMjudge is healthy"exit 0Contest Day Checklist
Before the contest starts:
- ✓ Test all problems with sample solutions
- ✓ Verify all programming languages work
- ✓ Check judgehost status and connectivity
- ✓ Backup database and submissions
- ✓ Test team logins
- ✓ Verify scoreboard display
- ✓ Configure scoreboard freeze time
- ✓ Prepare balloon tracking
- ✓ Test clarification system
- ✓ Have admin contact information ready
During the contest:
- Monitor judgment queue for backlog
- Watch for judgehost failures
- Respond to clarifications promptly
- Track balloon deliveries
- Monitor for suspicious activity
- Be ready to rejudge if needed
Resource Allocation
Recommended resources by contest size:
Small Contest (< 50 teams):
- Domserver: 2GB RAM, 2 vCPU
- Judgehost: 4GB RAM, 4 vCPU (1 host)
- Database: 2GB RAM, 2 vCPU
- Storage: 50GB
Medium Contest (50-200 teams):
- Domserver: 4GB RAM, 4 vCPU
- Judgehost: 8GB RAM, 8 vCPU (2-3 hosts)
- Database: 4GB RAM, 4 vCPU
- Storage: 100GB
Large Contest (200+ teams):
- Domserver: 8GB RAM, 8 vCPU
- Judgehost: 16GB RAM, 16 vCPU (5+ hosts)
- Database: 8GB RAM, 8 vCPU
- Storage: 200GB+
Troubleshooting
Submissions Stuck in Queue
Symptoms: Submissions remain in “pending” status indefinitely
Solutions:
- Check Judgehost Status:
# View judgehost logsdocker logs domjudge-judgehost
# Check if judgehost is registeredcurl https://your-app.klutch.sh/api/judgehosts- Verify Judgehost Credentials:
# Test authenticationcurl -u judgehost:password https://your-app.klutch.sh/api/info- Restart Judgehost:
docker restart domjudge-judgehost- Check Database Connection:
mysql -h $MYSQL_HOST -P 8000 -u $MYSQL_USER -p$MYSQL_PASSWORD \ -e "SELECT * FROM judgehost" domjudgeCompile Errors for Valid Code
Symptoms: Code that should compile fails with compiler errors
Solutions:
-
Check Language Configuration:
- Navigate to “Configuration” → “Languages”
- Verify compiler path and flags
- Test with known-good code
-
Verify Compiler Installation:
# Inside judgehost containergcc --versiong++ --versionpython3 --versionjavac -version- Check Compile Time Limit:
// May need to increase for large files$default_compile_time = 60; // seconds- Review Compile Script:
# Check compile script for languagecat /opt/domjudge/judgehost/etc/compile/*.shWrong Answer for Correct Solutions
Symptoms: Solutions that should be accepted receive “Wrong Answer”
Solutions:
-
Verify Test Cases:
- Check input/output files match expected format
- Verify no trailing whitespace issues
- Check for correct line endings (LF vs CRLF)
-
Check Output Validator:
- Use default validator for exact match
- Configure custom validator if needed
- Test validator manually
-
Review Problem Settings:
- Verify time/memory limits are appropriate
- Check if problem uses special judge
-
Compare Outputs:
# View submission output# Navigate to submission → Click "View output"# Compare with expected outputTime Limit Exceeded
Symptoms: Solutions timeout even with efficient algorithms
Solutions:
-
Increase Time Limit:
- Navigate to problem settings
- Increase time limit if appropriate
- Consider language-specific multipliers
-
Check Language Multiplier:
// Java typically needs 2x time'java' => [ 'time_multiplier' => 2, 'memory_multiplier' => 2,]-
Verify Test Case Size:
- Large inputs may need more time
- Optimize test cases if possible
-
Check CPU Allocation:
- Ensure judgehost has sufficient CPU
- Monitor CPU usage during judging
Database Connection Failures
Symptoms: “Could not connect to database” errors
Solutions:
- Verify Database is Running:
# Check database statusmysql -h $MYSQL_HOST -P 8000 -u $MYSQL_USER -p$MYSQL_PASSWORD -e "SELECT 1"- Check Connection String:
# Verify environment variablesecho $MYSQL_HOSTecho $MYSQL_PORTecho $MYSQL_DATABASE- Test Network Connectivity:
# From domserver containertelnet $MYSQL_HOST 8000- Review Database Logs:
# Check MariaDB logs for connection errorsdocker logs mariadb-containerScoreboard Not Updating
Symptoms: Scoreboard shows stale data
Solutions:
- Clear Cache:
# Clear scoreboard cacherm -rf /tmp/domjudge_cache/scoreboard*- Check Scoreboard Events:
-- Verify events are being loggedSELECT * FROM event ORDER BY eventtime DESC LIMIT 10;-
Force Scoreboard Refresh:
- Navigate to “Scoreboard”
- Click “Refresh” button
- Or press Ctrl+F5 in browser
-
Check Contest Status:
- Verify contest is active
- Check if scoreboard is frozen
Advanced Configuration
Custom Problem Validators
Create custom output validators for complex problems:
- Create Validator Script:
#!/usr/bin/env python3import sys
def validate(judge_output, team_output): """ Compare team output with judge output Return True if correct, False otherwise """ judge_lines = judge_output.strip().split('\n') team_lines = team_output.strip().split('\n')
if len(judge_lines) != len(team_lines): return False
for judge, team in zip(judge_lines, team_lines): # Custom comparison logic if not compare_line(judge, team): return False
return True
def compare_line(judge, team): # Implement custom comparison # e.g., floating point comparison with epsilon try: judge_val = float(judge) team_val = float(team) return abs(judge_val - team_val) < 1e-6 except: return judge.strip() == team.strip()
if __name__ == '__main__': judge_output = open(sys.argv[1]).read() team_output = sys.stdin.read()
if validate(judge_output, team_output): sys.exit(42) # Correct else: sys.exit(43) # Wrong Answer- Upload Validator:
- Navigate to problem settings
- Upload validator as executable
- Configure validator options
Multi-Site Contests
Configure DOMjudge for distributed contests:
-
Setup Shadow Mode: Allow teams to submit on multiple sites
-
Configure External API:
$enable_external_api = true;$external_api_url = 'https://main-site.example.com/api';$external_api_key = 'your-api-key';- Sync Contest Data: Use API to sync:
- Contest configuration
- Problem sets
- Teams (if needed)
Custom Scoring Rules
Implement custom scoring systems:
-
Modify Scoring Logic: Edit scoring calculations
-
Penalty Time: Adjust penalty for wrong submissions:
$penalty_time = 20; // minutes per wrong submission- First Solve Bonus: Award points for first solve:
$first_solve_bonus = 10; // bonus pointsAPI Integration
Integrate external systems via DOMjudge API:
-
Generate API Token:
- Navigate to user profile
- Generate API key
- Store securely
-
Fetch Contest Data:
# Get contest infocurl -u admin:token https://your-app.klutch.sh/api/contests
# Get scoreboardcurl https://your-app.klutch.sh/api/scoreboard
# Get submissionscurl -u admin:token https://your-app.klutch.sh/api/submissions- Submit via API:
import requests
url = "https://your-app.klutch.sh/api/submissions"auth = ('team1', 'password')files = {'code': open('solution.cpp', 'rb')}data = { 'problem': 'A', 'language': 'cpp',}
response = requests.post(url, auth=auth, files=files, data=data)print(response.json())Automated Testing
Create automated test suites for problems:
#!/bin/bashPROBLEM="A"SOLUTIONS_DIR="./solutions/$PROBLEM"
for solution in $SOLUTIONS_DIR/*.cpp; do echo "Testing $solution..."
# Compile g++ -o test_prog $solution
# Run against test cases for input in ./testcases/$PROBLEM/*.in; do output="${input%.in}.ans" result=$(./test_prog < $input) expected=$(cat $output)
if [ "$result" = "$expected" ]; then echo " ✓ $(basename $input): PASS" else echo " ✗ $(basename $input): FAIL" fi done
rm test_progdoneAdditional Resources
- Official DOMjudge Website
- DOMjudge Documentation
- DOMjudge GitHub Repository
- Problem Package Format
- ICPC Official Website
- Klutch.sh Documentation
- Persistent Storage Guide
- Networking Configuration
Conclusion
DOMjudge provides a professional, battle-tested platform for running programming contests of any scale. By deploying on Klutch.sh, you benefit from automatic HTTPS, persistent storage, and simple Docker-based deployment while maintaining the robust security and performance that competitive programming events demand.
The platform’s proven track record in ICPC regional and world finals demonstrates its reliability under pressure. Features like isolated sandbox execution, support for multiple languages, real-time scoreboards, and comprehensive contest management tools make DOMjudge the go-to choice for programming contest organizers worldwide.
Whether you’re running a small university practice contest or a major international competition, DOMjudge scales to meet your needs. The system’s modular architecture allows you to deploy multiple judgehosts for parallel judging, while the comprehensive API enables integration with external systems and custom workflows.
Start with the basic configuration outlined in this guide, then expand functionality through custom validators, automated testing, and multi-site configurations as your contest requirements grow. Your contest data remains secure, the judging process is transparent and fair, and the open-source nature of DOMjudge ensures you have complete control over your competition platform.
Deploy DOMjudge today and provide contestants with a world-class programming competition experience that rivals the largest international contests.