A cross-platform online judge system based on Spring framework and ActiveMQ.
Official Demo · Project Page · Change Log
Introduction
Verwandlung Online Judge is a self-hostable, open-source online judge for competitive programming. It presents algorithmic problems, accepts submissions in multiple languages, then compiles, runs and evaluates them against predefined test cases, reporting correctness, run time and memory usage.
Highlights:
- Cross-platform native judging. Submissions are judged natively on Linux
- Sandboxed execution. On Linux each submission runs under kernel-enforced
- Real-time feedback streamed to the browser with Server-Sent Events (SSE), so
- Horizontally scalable judging. The web app and the judgers are decoupled
- Contests and internationalization (English and Chinese) out of the box.
Architecture
Verwandlung has two components, the web application and one or more judgers, communicating over ActiveMQ:

Getting Started
Run with Docker (recommended)
The fastest way to stand up the whole stack (database, message queue, web app and a judger) — two equivalent options:
git clone https://github.com/hzxie/voj.git
cd voj
A) Docker Compose
docker compose up -d
B) Plain docker run, no compose
scripts/run-docker.sh
Both bring up the voj.web and voj.judger containers and persist the database in a named volume, so the web UI is available at run-docker.sh also generates strong random secrets and prints them once; to run the prebuilt Docker Hub images instead of building locally, use VOJ_PULL=1 scripts/run-docker.sh (or docker compose up, which pulls by default).
Configuration (database, mail, URLs, the judger token) is read from VOJ_* environment variables at runtime, so you can reconfigure or sit behind a reverse proxy without rebuilding. See docker/README.md for the full configuration, persistence and reverse-proxy details.
Build from source
Web application produces a self-contained executable JAR (embedded Tomcat):
cd web
mvn package -DskipTests # -> web/target/voj.web.jar
java -jar target/voj.web.jar
Judger builds a Spring Boot jar plus the JNI native library, so a C++ toolchain is required:
# Linux build prerequisites: g++, make and libseccomp
sudo apt-get install -y g++ make libseccomp-dev
cd judger mvn package -DskipTests # -> judger/target/voj.judger.jar
On some JDK layouts the JNI build needsjnimd.h/jawtmd.hcopied from
$JAVAHOME/include/linux(or.../win32) up into$JAVAHOME/include.
Convenience wrappers live in scripts/: build-jars.sh, build-docker.sh, run-web.sh and run-judger.sh (the run scripts preflight MySQL/ActiveMQ and import sql/schema.sql, sql/seed.sql and sql/demo.sql on first launch).
Both components read a voj.properties file (database, mail, message queue, sandbox, etc.). See docs/configuration.md for the full property reference.
Requirements
| Component | Requirement | | --- | --- | | Java | JDK 17+ (tested through JDK 25) | | Database | MySQL 8.0+ or MariaDB 10.4+ | | Message queue | ActiveMQ 6.0+ (Jakarta JMS) | | Judger (Linux build) | GCC/g++, make, libseccomp | | Judger OS | Linux or Windows |
Security model
Untrusted submissions run inside a sandbox so they cannot harm the judging host:
- Linux (native). Each submission runs in a forked child with
setrlimit
wait4/getrusage, a private network namespace, a seccomp syscall
filter (blocking ptrace, mount, module loading, etc.) and privileges dropped to
an unprivileged user (system.username, defaulting to nobody). If the judger
runs as root and cannot drop privileges, the submission is refused rather than run
as root.
- Linux (isolate). Optionally set
judger.sandbox = isolateto delegate to the
- Windows (native). Each submission runs under a separate low-privilege Windows
CreateProcessWithLogonW) inside a
Job Object
that caps the process count (anti fork-bomb), enforces memory and wall-clock
limits, applies UI restrictions (no access to the interactive desktop or
clipboard), and kills the whole process tree when the run ends.
The native sandbox does not provide full filesystem isolation. Reference answers and configuration are protected by file permissions (the work directory is handed to the sandbox user, while the checkpoint directory stays readable only by the judger), but a submission can still read world-readable host files. For complete filesystem isolation, run the judger as root with judger.sandbox = isolate, which confines each submission to its own mount namespace.
For shared, multi-tenant hosts, create a dedicated unprivileged account for system.username instead of reusing nobody. See docs/deployment.md for the filesystem layout and permissions a root deployment expects.
The origin of "Verwandlung"
In 2011, LinkedIn released the Kafka message queue, named after the author Franz Kafka. A year later, Alibaba introduced MetaQ, built on Kafka, a nod to Kafka's novella Metamorphosis. Since the message queue is central to this application, it took the name "Verwandlung", the German title of Metamorphosis.
Contributing
Contributions are welcome:
- Report bugs and request features on the issues page.
- For new features, please open an issue to discuss before sending a pull request.
- Translators for additional languages are especially appreciated.
License
This project is open-sourced under the GNU GPL v3.