Skip to content

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

  1. Team submits solution through web interface
  2. Submission enters judgment queue
  3. Judgehost picks up submission from queue
  4. Code is compiled with appropriate compiler
  5. Compiled program runs against each test case
  6. Output is compared with expected output
  7. Verdict is determined (Accepted, Wrong Answer, Time Limit, etc.)
  8. Result is stored in database and displayed to team
  9. If accepted, balloon notification is generated
  10. 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 variables
ENV CONTAINER_TIMEZONE=UTC \
MYSQL_HOST=mysql \
MYSQL_DATABASE=domjudge \
MYSQL_USER=domjudge \
MYSQL_PASSWORD=domjudge \
MYSQL_ROOT_PASSWORD=rootpassword
# Install additional dependencies
RUN apt-get update && apt-get install -y \
curl \
vim \
zip \
unzip \
&& rm -rf /var/lib/apt/lists/*
# Create necessary directories
RUN mkdir -p /domjudge/submissions \
/domjudge/output \
/domjudge/tmp
# Set proper permissions
RUN chown -R www-data:www-data /domjudge
# Copy custom configuration
COPY domserver-config.php /opt/domjudge/domserver/etc/domserver-config.php
# Expose HTTP port
EXPOSE 80
# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
CMD curl -f http://localhost/api/info || exit 1
# Use default entrypoint from base image

Step 2: Create the Dockerfile for Judgehost

Create Dockerfile.judgehost for the judging daemon:

FROM domjudge/judgehost:latest
# Set environment variables
ENV CONTAINER_TIMEZONE=UTC \
DOMSERVER_BASEURL=http://domserver/ \
JUDGEDAEMON_USERNAME=judgehost \
JUDGEDAEMON_PASSWORD=password
# Install additional language support
RUN 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 directories
RUN mkdir -p /judgings
# Set proper permissions
RUN chown -R domjudge:domjudge /judgings
# Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD pgrep -f judgedaemon || exit 1
# Use default entrypoint from base image

Step 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
// Timezone
date_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/bash
set -e
# Wait for MySQL to be ready
echo "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 2
done
echo "MySQL is ready!"
# Create database and user if they don't exist
mysql -h "$MYSQL_HOST" -u root -p"$MYSQL_ROOT_PASSWORD" <<EOF
CREATE 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 empty
TABLES=$(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!"
fi

Step 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

Terminal window
git init
git add Dockerfile Dockerfile.judgehost domserver-config.php init-db.sh docker-compose.yml
git commit -m "Initial DOMjudge deployment configuration"

Step 7: Test Locally

Before deploying to Klutch.sh, test locally:

Terminal window
# Build and start containers
docker-compose up -d
# Check logs
docker-compose logs -f domserver
docker-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:

  1. Navigate to klutch.sh/app
  2. Click "New Project"
  3. Select MariaDB or use a custom Dockerfile
  4. Configure database: - Database name: `domjudge` - Username: `domjudge` - Password: Create a secure password - Root password: Create a secure root password
  5. Select **TCP** as traffic type
  6. Set internal port to **3306**
  7. Add persistent storage with mount path: `/var/lib/mysql` and size: `20GB`
  8. 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:

Terminal window
git remote add origin https://github.com/yourusername/domjudge-klutch.git
git branch -M master
git push -u origin master

Step 3: Deploy Domserver to Klutch.sh

  1. Navigate to klutch.sh/app
  2. Click "New Project" and select "Import from GitHub"
  3. Authorize Klutch.sh to access your GitHub repositories
  4. Select your DOMjudge domserver repository
  5. Klutch.sh will automatically detect the Dockerfile

Step 4: Configure Domserver Traffic Settings

  1. In the project settings, select **HTTP** as the traffic type
  2. Set the internal port to **80**
  3. Klutch.sh will automatically provision an HTTPS endpoint

Step 5: Add Persistent Storage for Domserver

DOMjudge requires persistent storage for submissions and output:

  1. In your project settings, navigate to the "Storage" section
  2. Add a volume with mount path: `/domjudge/submissions` and size: `50GB` (for submissions)
  3. 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: domjudge
  • MYSQL_USER: domjudge
  • MYSQL_PASSWORD: Your database password
  • MYSQL_ROOT_PASSWORD: Your root password
  • CONTAINER_TIMEZONE: UTC (or your timezone)

Step 7: Deploy Domserver

  1. Review your configuration settings
  2. Click "Deploy" to start the deployment
  3. Monitor build logs for any errors
  4. Once deployed, domserver will be available at `your-app.klutch.sh`

For the judging daemon, create a separate deployment:

  1. Create a new repository with `Dockerfile.judgehost` renamed to `Dockerfile`
  2. Push to GitHub and import to Klutch.sh
  3. Configure environment variables: - `DOMSERVER_BASEURL`: `https://your-app.klutch.sh` - `JUDGEDAEMON_USERNAME`: `judgehost` - `JUDGEDAEMON_PASSWORD`: Create secure password - `CONTAINER_TIMEZONE`: `UTC`
  4. Add persistent storage with mount path: `/judgings` and size: `20GB`
  5. Select **HTTP** traffic (for health checks)
  6. 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:

  1. Access DOMjudge: Navigate to https://your-app.klutch.sh

  2. First Login: Use default credentials:

    • Username: admin
    • Password: password (or check container logs)
  3. Change Admin Password:

    • Click “admin” in top-right corner
    • Select “Change Password”
    • Enter a strong password
    • Save changes
  4. 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:

  1. Navigate to "Configuration" → "Contests"
  2. Click "Add new contest"
  3. 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
  4. Click "Save" to create contest

Adding Problems

Add problems to your contest:

  1. Navigate to "Problems"
  2. Click "Add new problem"
  3. Configure problem: - **Label**: A, B, C, etc. - **Name**: "Hello World", "Sum Two Numbers" - **Time Limit**: 1 second (typical) - **Memory Limit**: 256 MB (typical)
  4. 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
  5. 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 World
time_limit: 1.0
memory_limit: 256

Registering Teams

Add teams to participate:

  1. Navigate to "Teams"
  2. Click "Add new team"
  3. Configure team: - **Name**: "Team Alpha" - **Category**: Select category (University, High School, etc.) - **Affiliation**: University/Organization name - **Members**: Team member names
  4. 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
  5. Provide credentials to team

Bulk import teams via TSV:

teams 1
1 TeamName1 University A Category1
2 TeamName2 University B Category1

Creating Team Categories

Organize teams by category:

  1. Navigate to "Configuration" → "Team Categories"
  2. Click "Add new category"
  3. Set category name: "University", "High School", "Industry"
  4. Set visibility and color
  5. Save category

Configuring Languages

Enable programming languages:

  1. Navigate to "Configuration" → "Languages"
  2. Enable languages: - C (gcc) - C++ (g++) - Java (OpenJDK) - Python 3 - Kotlin - Go - Rust
  3. Set time/memory multipliers if needed (e.g., Java gets 2x time)
  4. Test each language with a sample submission

Testing Submissions

Test the judging system:

  1. Login as a Team: Use team credentials

  2. View Problems: Click “Problems” to see available problems

  3. 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;
}
  1. View Submission: Navigate to “Submissions” to see status

  2. Check Verdict: Should show “Correct” if output matches

Managing Clarifications

Handle team questions during contest:

  1. Teams Request Clarifications:

    • Team clicks “Request Clarification”
    • Selects problem (or general)
    • Enters question
    • Submits request
  2. 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:

  1. Scoreboard: Real-time standings

    • Navigate to “Scoreboard”
    • View team rankings
    • See problem status (attempts, first solve)
    • Monitor freeze status
  2. Submissions: All team submissions

    • Navigate to “Submissions”
    • Filter by problem, team, verdict
    • Rejudge if needed
  3. Balloon Tracking:

    • Navigate to “Balloons”
    • See which teams solved problems
    • Mark balloons as delivered
    • Track first solves
  4. Judging Queue:

    • Navigate to “Judging Runs”
    • Monitor judgehost activity
    • Check for stuck judgments

Production Best Practices

Security Hardening

  1. Change Default Passwords: Immediately change all default passwords:
Terminal window
# Generate strong passwords
openssl rand -base64 32

Update:

  • Admin password
  • Judgehost password
  • Database passwords
  1. 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'];
  1. HTTPS Only: Ensure all connections use HTTPS (automatic with Klutch.sh)

  2. Disable Self-Registration:

$allow_self_registration = false;
  1. Rate Limiting: Prevent submission flooding:
$submission_rate_limit = 60; // Max submissions per hour per team
  1. Secure Database Connection: Use strong database passwords and restrict access:
Terminal window
# In MariaDB
REVOKE ALL PRIVILEGES ON *.* FROM 'domjudge'@'%';
GRANT ALL PRIVILEGES ON domjudge.* TO 'domjudge'@'%';

Performance Optimization

  1. Database Tuning: Optimize MariaDB configuration:
# my.cnf
[mysqld]
innodb_buffer_pool_size = 4G
innodb_log_file_size = 512M
max_connections = 200
query_cache_size = 256M
tmp_table_size = 256M
max_heap_table_size = 256M
  1. Multiple Judgehosts: Deploy multiple judgehost instances for parallel judging:
Terminal window
# Scale judgehosts based on contest size
# Small: 1-2 judgehosts
# Medium: 3-5 judgehosts
# Large: 5-10+ judgehosts
  1. Scoreboard Caching: Enable caching for scoreboard:
$scoreboard_cache_ttl = 30; // Cache for 30 seconds
  1. Problem Package Optimization: Minimize test case sizes:
    • Compress test data
    • Remove unnecessary files
    • Optimize validator performance

Backup Strategy

Implement comprehensive backups:

  1. Database Backups: Schedule regular MySQL dumps:
backup-domjudge.sh
#!/bin/bash
BACKUP_DIR="/backups/domjudge_$(date +%Y%m%d_%H%M%S)"
mkdir -p $BACKUP_DIR
# Backup database
mysqldump -h $MYSQL_HOST -P 8000 -u $MYSQL_USER -p$MYSQL_PASSWORD \
$MYSQL_DATABASE > $BACKUP_DIR/domjudge.sql
# Compress backup
gzip $BACKUP_DIR/domjudge.sql
# Keep only last 30 days
find /backups -name "domjudge_*.sql.gz" -mtime +30 -delete
  1. Submissions Backup: Archive all submissions:
Terminal window
# Backup submissions directory
tar -czf $BACKUP_DIR/submissions.tar.gz /domjudge/submissions
  1. Configuration Backup: Save configuration files:
Terminal window
# Backup config
cp /opt/domjudge/domserver/etc/domserver-config.php $BACKUP_DIR/
  1. Automated Backup Schedule:
Terminal window
# Add to crontab
0 2 * * * /usr/local/bin/backup-domjudge.sh

Monitoring and Logging

  1. Enable Detailed Logging:
// In domserver-config.php
$log_level = 'DEBUG';
$enable_audit_log = true;
  1. Monitor Judgehost Activity:
Terminal window
# Check judgehost logs
tail -f /var/log/domjudge/judgehost.log
# Monitor judgment queue
watch -n 5 'mysql -h $MYSQL_HOST -u $MYSQL_USER -p$MYSQL_PASSWORD \
-e "SELECT COUNT(*) FROM judging WHERE result IS NULL" domjudge'
  1. Database Monitoring:
-- Active connections
SHOW PROCESSLIST;
-- Slow queries
SELECT * FROM information_schema.PROCESSLIST
WHERE TIME > 5 ORDER BY TIME DESC;
-- Database size
SELECT table_schema AS "Database",
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS "Size (MB)"
FROM information_schema.TABLES
GROUP BY table_schema;
  1. Health Check Script:
health-check.sh
#!/bin/bash
DOMSERVER_URL="https://your-app.klutch.sh"
# Check domserver
if ! curl -f -s -o /dev/null "$DOMSERVER_URL/api/info"; then
echo "ERROR: Domserver not responding"
exit 1
fi
# Check judgehosts
ACTIVE_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 0

Contest Day Checklist

Before the contest starts:

  1. ✓ Test all problems with sample solutions
  2. ✓ Verify all programming languages work
  3. ✓ Check judgehost status and connectivity
  4. ✓ Backup database and submissions
  5. ✓ Test team logins
  6. ✓ Verify scoreboard display
  7. ✓ Configure scoreboard freeze time
  8. ✓ Prepare balloon tracking
  9. ✓ Test clarification system
  10. ✓ Have admin contact information ready

During the contest:

  1. Monitor judgment queue for backlog
  2. Watch for judgehost failures
  3. Respond to clarifications promptly
  4. Track balloon deliveries
  5. Monitor for suspicious activity
  6. 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:

  1. Check Judgehost Status:
Terminal window
# View judgehost logs
docker logs domjudge-judgehost
# Check if judgehost is registered
curl https://your-app.klutch.sh/api/judgehosts
  1. Verify Judgehost Credentials:
Terminal window
# Test authentication
curl -u judgehost:password https://your-app.klutch.sh/api/info
  1. Restart Judgehost:
Terminal window
docker restart domjudge-judgehost
  1. Check Database Connection:
Terminal window
mysql -h $MYSQL_HOST -P 8000 -u $MYSQL_USER -p$MYSQL_PASSWORD \
-e "SELECT * FROM judgehost" domjudge

Compile Errors for Valid Code

Symptoms: Code that should compile fails with compiler errors

Solutions:

  1. Check Language Configuration:

    • Navigate to “Configuration” → “Languages”
    • Verify compiler path and flags
    • Test with known-good code
  2. Verify Compiler Installation:

Terminal window
# Inside judgehost container
gcc --version
g++ --version
python3 --version
javac -version
  1. Check Compile Time Limit:
// May need to increase for large files
$default_compile_time = 60; // seconds
  1. Review Compile Script:
Terminal window
# Check compile script for language
cat /opt/domjudge/judgehost/etc/compile/*.sh

Wrong Answer for Correct Solutions

Symptoms: Solutions that should be accepted receive “Wrong Answer”

Solutions:

  1. Verify Test Cases:

    • Check input/output files match expected format
    • Verify no trailing whitespace issues
    • Check for correct line endings (LF vs CRLF)
  2. Check Output Validator:

    • Use default validator for exact match
    • Configure custom validator if needed
    • Test validator manually
  3. Review Problem Settings:

    • Verify time/memory limits are appropriate
    • Check if problem uses special judge
  4. Compare Outputs:

Terminal window
# View submission output
# Navigate to submission → Click "View output"
# Compare with expected output

Time Limit Exceeded

Symptoms: Solutions timeout even with efficient algorithms

Solutions:

  1. Increase Time Limit:

    • Navigate to problem settings
    • Increase time limit if appropriate
    • Consider language-specific multipliers
  2. Check Language Multiplier:

// Java typically needs 2x time
'java' => [
'time_multiplier' => 2,
'memory_multiplier' => 2,
]
  1. Verify Test Case Size:

    • Large inputs may need more time
    • Optimize test cases if possible
  2. Check CPU Allocation:

    • Ensure judgehost has sufficient CPU
    • Monitor CPU usage during judging

Database Connection Failures

Symptoms: “Could not connect to database” errors

Solutions:

  1. Verify Database is Running:
Terminal window
# Check database status
mysql -h $MYSQL_HOST -P 8000 -u $MYSQL_USER -p$MYSQL_PASSWORD -e "SELECT 1"
  1. Check Connection String:
Terminal window
# Verify environment variables
echo $MYSQL_HOST
echo $MYSQL_PORT
echo $MYSQL_DATABASE
  1. Test Network Connectivity:
Terminal window
# From domserver container
telnet $MYSQL_HOST 8000
  1. Review Database Logs:
Terminal window
# Check MariaDB logs for connection errors
docker logs mariadb-container

Scoreboard Not Updating

Symptoms: Scoreboard shows stale data

Solutions:

  1. Clear Cache:
Terminal window
# Clear scoreboard cache
rm -rf /tmp/domjudge_cache/scoreboard*
  1. Check Scoreboard Events:
-- Verify events are being logged
SELECT * FROM event ORDER BY eventtime DESC LIMIT 10;
  1. Force Scoreboard Refresh:

    • Navigate to “Scoreboard”
    • Click “Refresh” button
    • Or press Ctrl+F5 in browser
  2. Check Contest Status:

    • Verify contest is active
    • Check if scoreboard is frozen

Advanced Configuration

Custom Problem Validators

Create custom output validators for complex problems:

  1. Create Validator Script:
validator.py
#!/usr/bin/env python3
import 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
  1. Upload Validator:
    • Navigate to problem settings
    • Upload validator as executable
    • Configure validator options

Multi-Site Contests

Configure DOMjudge for distributed contests:

  1. Setup Shadow Mode: Allow teams to submit on multiple sites

  2. Configure External API:

$enable_external_api = true;
$external_api_url = 'https://main-site.example.com/api';
$external_api_key = 'your-api-key';
  1. Sync Contest Data: Use API to sync:
    • Contest configuration
    • Problem sets
    • Teams (if needed)

Custom Scoring Rules

Implement custom scoring systems:

  1. Modify Scoring Logic: Edit scoring calculations

  2. Penalty Time: Adjust penalty for wrong submissions:

$penalty_time = 20; // minutes per wrong submission
  1. First Solve Bonus: Award points for first solve:
$first_solve_bonus = 10; // bonus points

API Integration

Integrate external systems via DOMjudge API:

  1. Generate API Token:

    • Navigate to user profile
    • Generate API key
    • Store securely
  2. Fetch Contest Data:

Terminal window
# Get contest info
curl -u admin:token https://your-app.klutch.sh/api/contests
# Get scoreboard
curl https://your-app.klutch.sh/api/scoreboard
# Get submissions
curl -u admin:token https://your-app.klutch.sh/api/submissions
  1. 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:

test-problem.sh
#!/bin/bash
PROBLEM="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_prog
done

Additional Resources

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.