Judge0 in four containers: what the self-hosted deployment asks of you
Robust, fast, scalable, and sandboxed open-source online code execution system for humans and AI.
At a glance
- What is it?
- Judge0 is an open-source online code execution system, self-hostable through a compose file that runs an API server, a worker, Postgres and Redis on port 2358. The deployment is readable in one sitting, and it also tells you exactly how much isolation you are getting.
- Who is it for?
- Judge0 earns a place in an agent or assessment pipeline that needs isolated execution behind a stable JSON contract, and it charges for that with a privileged container, a passwordless sudo user and a floating :latest tag unless you pin it. It is the wrong choice for a network where untrusted compiled code may not run at all.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 20 days ago.
- What is it written in?
- Mainly HTML, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- judge0/judge04,518 ★ · +92
| Repository | 2026-09-10 | 2026-11-13 |
|---|---|---|
| judge0/judge0 | 4.4k | 4.5k |
92 Stars in the last 64 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Four containers, one published port
docker-compose.yml is short enough to read in one sitting, and it is the real deployment documentation. Four services come up: a server, a worker whose command is ./scripts/workers, a Postgres 16.2 database and a Redis 7.2.4 instance. All four carry restart: always, and all four inherit the same json-file logging block capped at 100M. The server and worker share one image, judge0/judge0:latest, and both mount ./judge0.conf at /judge0.conf read only:
server:
image: judge0/judge0:latest
volumes:
- ./judge0.conf:/judge0.conf:ro
ports:
- "2358:2358"
privileged: trueOne port is published, so every client request arrives through 2358 while Postgres and Redis stay on the compose network. The database is the only service with a named volume, data mounted at /var/lib/postgresql/data/, so accounts and stored submissions survive a docker compose down. The worker is a separate service because compiling and running someone else's program is the expensive half of the work and deserves to scale apart from the API.
The worker runs privileged, and judge0 gets passwordless sudo
The isolation story is visible in two files, and it deserves reading before an instance is exposed. Both the server and the worker carry privileged: true. The image then hands its runtime user unrestricted sudo:
RUN useradd -u 1000 -m -r judge0 && \
echo "judge0 ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers && \
chown judge0: /api/tmp/
USER judge0The container boundary is therefore not what separates submitted code from the host. That job falls to the compiler base image, judge0/compilers:1.4.0, and to keeping execution in a worker service that is separable from the API. The same Dockerfile installs cron, libpq-dev and sudo, copies the cron directory into /etc/cron.d, installs bundler 2.1.4 alongside aglio 2.3.0, and runs `bundle` in /api with RAILS_ENV=production. Read this as an operator: put instances on infrastructure you can rebuild, and treat SECURITY.md and PRIVACY_POLICY.md as deployment files rather than paperwork.
One POST to /submissions?wait=true is the whole read path
The HTTP JSON API is small on purpose. A submission is one POST carrying language_id, source_code and stdin, and wait=true asks for the finished result in the same response instead of a token you poll for:
curl \
-H "Content-Type: application/json" \
-d '{
"language_id": 109,
"source_code": "print(f\"hello, {input()}\")",
"stdin": "Alice"
}' \
"https://ce.judge0.com/submissions?wait=true"That request points at the hosted CE endpoint, ce.judge0.com, not at a self-hosted instance. The Python SDK wraps the same call and comes from pip:
# pip install judge0
import judge0
result = judge0.run(source_code="print(f'hello, {input()}')", stdin="Alice", language=judge0.PYTHON)
print(result.stdout)Everything past those three fields, custom compiler options, command-line arguments, time and memory limits, extra files, webhooks for HTTP callbacks, is specified on ce.judge0.com rather than in this repository.
Two flavors on two branches, while compose pulls :latest
Judge0 comes in two flavors, Judge0 CE and Judge0 Extra CE, and they differ mostly in supported languages. The code lives on separate branches: master for CE, the extra branch for Extra CE. v1.13.1 and v1.13.1-extra were both published on 2024-04-18, and the release before them, v1.13.0 (Judge0 CE), dates from 2021-03-10. The Dockerfile still stamps ENV JUDGE0_VERSION "1.13.1" while docker-compose.yml pins nothing and asks for judge0/judge0:latest.
That gap is the operational detail. The Dockerfile declares 1.13.1, while the image selected by the compose file floats with the registry’s `latest` tag; the Dockerfile value alone does not identify a pulled image. Pin the image by digest to identify exact image content, since a version tag can also move. Two branches also means an Extra CE deployment has to check out the extra branch before any of this applies.
judge0.conf reaches the stack as a mount and as env vars
A single file carries the configuration, and it arrives by two different routes. The server and worker read it as a file mounted at /judge0.conf with :ro, while the database and Redis read the same file as environment variables through env_file. The Redis service shows the mechanics, because its password has to reach a container command:
redis:
image: redis:7.2.4
command: [
"bash", "-c",
'docker-entrypoint.sh --appendonly no --requirepass "$$REDIS_PASSWORD"'
]
env_file: judge0.confThe doubled dollar sign passes the variable through compose instead of expanding it locally, so REDIS_PASSWORD has to exist inside judge0.conf rather than in your shell. Two consequences. Redis is started with --appendonly no, so append-only persistence is off, which makes the Postgres data volume the only durable state named in this file. And Postgres takes the same env_file treatment:
db:
image: postgres:16.2
env_file: judge0.conf
volumes:
- data:/var/lib/postgresql/data/Ninety plus languages, multi-file projects, and where the parameters live
The feature list is where Judge0 is broadest and least concrete. It claims support for 90+ languages, compilation and execution of multi-file programs rather than single files only, additional files supplied alongside a user's program, custom user-defined compiler options, command-line arguments, time and memory limits, detailed execution results, and webhooks for HTTP callbacks. Each of those is a reason to pick an execution service, and none of them is specified inside this repository. The language list lives at ide.judge0.com, the API parameters at ce.judge0.com, and the README hands you both as links.
What the repository does hold is the Ruby side: Gemfile, Gemfile.lock, config.ru, Rakefile, .rspec and .rubocop.yml at the top level, alongside app/, config/, db/, lib/, spec/, scripts/, cron/, docs/ and vendor/. A spec directory with .rspec means the server carries its own test suite, which matters if you fork it, and TELEMETRY.md sits next to PRIVACY_POLICY.md in the root. Judge0 IDE, the free open-source editor at ide.judge0.com, is the quick way to see whether the execution model fits a task before installing anything.
Hosted plans, GPL-3.0, and no published prices
Three routes in, all named on the README page: Judge0 Cloud through Rapid, Judge0 Cloud by working with Judge0 directly, or self-hosting on your own infrastructure. The README links to judge0.com/#pricing for the hosted routes rather than stating prices, so a cost model for the cloud options has to come from the vendor. Self-hosting carries no licence fee, and the root LICENSE is GPL-3.0, which matters the moment you modify the server: derivative distribution obligations travel with it, and the vendored dependencies plus the compilers base image carry their own terms.
Where Judge0 fits badly is easier to state. It does not suit a network where untrusted compiled code may not run at all, since the whole design assumes a worker privileged enough to invoke compilers. It does not suit a task in a language missing from one of the two flavor lists, because flavor choice fixes the language set. The project dates from August 2016 and its own research paper compares the architecture against other online code execution systems and online judges, without settling the choice for you.
Editorial conclusion
Judge0 earns a place in an agent or assessment pipeline that needs isolated execution behind a stable JSON contract, and it charges for that with a privileged container, a passwordless sudo user and a floating :latest tag unless you pin it. It is the wrong choice for a network where untrusted compiled code may not run at all. Check three things first: the privileged and sudo lines in docker-compose.yml and the Dockerfile, whether the image version you would deploy is newer than the 1.13.1 stamped into the image, and whether the language you need appears on either flavor list.
Frequently asked questions
What is Judge0?
Judge0, pronounced like judge zero, is an open-source online code execution system established in August 2016. It is used to build applications that run code on demand, including AI agents, competitive programming platforms, e-learning platforms, candidate assessment and recruitment platforms, online code editors and online IDEs.
How does Judge0 work?
The self-hosted deployment runs four services from docker-compose.yml: an API server on port 2358, a worker started with ./scripts/workers, Postgres 16.2 for stored data and Redis 7.2.4. Both server and worker run the judge0/judge0 image with privileged set to true.
What is the Judge0 API?
Judge0 exposes a simple HTTP JSON API, documented at ce.judge0.com, plus an official Python SDK published as the judge0 package. A submission carries language_id, source_code and stdin, and the optional wait query parameter returns the finished result in the same response.
How do I use the Judge0 API?
POST your JSON payload to the submissions endpoint with wait=true, as in the curl example that posts language_id 109 to https://ce.judge0.com/submissions?wait=true. From Python, pip install judge0 and call judge0.run(source_code, stdin, language) with a constant such as judge0.PYTHON, then read result.stdout.
How do I install Judge0?
Self-hosting uses docker-compose.yml, which pulls judge0/judge0:latest for the server and worker, postgres:16.2 and redis:7.2.4, and mounts judge0.conf read only for the first two and as an env_file for the last two. A docker-compose.dev.yml sits beside it for development.
How much does Judge0 cost?
Judge0 offers a managed cloud through Rapid, a managed cloud sold directly, and self-hosting on your own infrastructure. The README points to judge0.com/#pricing for the hosted plans. Self-hosting carries no licence fee under GPL-3.0, only the infrastructure.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/judge0-judge0)Star history chart for your README
[](https://hysenlabs.com/projects/judge0-judge0)Was this useful?