Skip to content

Getting Started

Deploy NextJudge locally, register a user, submit a solution and confirm the judge returns a verdict.

  • Docker + Docker Compose
  • 4 GB RAM minimum (8 GB with web and judge together)
  • Ports free: 5000 (API), 5432 (Postgres), 5672 (RabbitMQ), 8080 (web)

Clone the repo. Commands below assume the repository root as the working directory.

./deploy.sh and ./dev-deploy.sh read secrets from .env in the repo root. Generate them once:

Terminal window
./.createenv.sh > .env

Required keys: JWT_SIGNING_SECRET, JUDGE_PASSWORD, WEB_BRIDGE_SECRET, DB_PASSWORD, RABBITMQ_USER, RABBITMQ_PASSWORD. Copy WEB_BRIDGE_SECRET into src/web/.env.local when running the web app outside Docker (see src/web/.env.example).

Backend only:

Terminal window
./deploy.sh

Backend + web UI:

Terminal window
./deploy.sh web

The first run builds the judge base image and can take several minutes. Later starts are faster.

Terminal window
curl -i http://localhost:5000/healthy

Expected response:

HTTP/1.1 200 OK

Connection refused, 502 or a hang usually means the data layer is not ready. Run docker ps and confirm Postgres, RabbitMQ, data layer and judge containers are running. Container names vary by compose file; health matters more than exact labels.

Web UI: http://localhost:8080. The first compile in dev mode can take a minute.


Deploy with ./deploy.sh web, then follow these steps.

Open http://localhost:8080 and sign up with email/password or GitHub (if configured in .env.dev).

To grant admin access, add your email to ADMIN_EMAILS before starting the stack, then register with that address. See Authentication for details.

Create an account and receive a token directly:

Terminal window
curl -s -X POST http://localhost:5000/v1/basic_register \
-H "Content-Type: application/json" \
-d '{"name":"ada","email":"ada@example.com","password":"example-password"}' \
| jq .

Copy token and id from the response. The walkthrough refers to them as $TOKEN and $USER_ID.

Terminal window
curl -s http://localhost:5000/v1/languages | jq '.[0:3]'

Note a language id (for example Python). The walkthrough calls it $LANG_ID.

No auth required. An empty array [] usually means the database did not seed. ./dev-deploy.sh sets SEED_DATA=true; plain ./deploy.sh may not. Admins can register languages later.

With $TOKEN set (raw JWT, no Bearer prefix):

Terminal window
curl -s http://localhost:5000/v1/problems \
-H "Authorization: $TOKEN" | jq '.[0] | {id, title, difficulty}'

If no problems appear, dev seed may not have run or you may be on a fresh production deploy. Create a problem in the admin UI or have an admin POST to /v1/problems. Note id as $PROBLEM_ID.

Terminal window
curl -s -X POST http://localhost:5000/v1/submissions \
-H "Authorization: $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"user_id\": \"$USER_ID\",
\"problem_id\": $PROBLEM_ID,
\"language_id\": \"$LANG_ID\",
\"source_code\": \"print('hello')\"
}" | jq '{id, status}'

Expect "status": "PENDING". Save id as $SUB_ID. The judge consumes the job from RabbitMQ.

Status poll (same path the web app uses while waiting):

Terminal window
curl -s http://localhost:5000/v1/submissions/$SUB_ID/status \
-H "Authorization: $TOKEN" | jq .

When status is no longer PENDING, fetch the full record:

Terminal window
curl -s http://localhost:5000/v1/submissions/$SUB_ID \
-H "Authorization: $TOKEN" | jq '{status, time_elapsed, stderr}'

Poll a few times. A few seconds in PENDING is normal. Minutes in PENDING indicate a pipeline problem (see Judge stuck on PENDING).

A non-PENDING status confirms the pipeline ran. ACCEPTED, WRONG_ANSWER and COMPILE_TIME_ERROR all prove the judge reached your submission. Compile errors on trivial code against a hard problem are expected.


Hot reload, mounted source and seed data:

Terminal window
./dev-deploy.sh web

Other flags: nojudge (skip judge), noelastic (skip Elasticsearch profile). See the Development guide for the full workflow.

Removes all local data:

Terminal window
./fully-reset.sh && ./deploy.sh web

Keep infrastructure in Docker and run a single process on the host:

ServiceCommand
Data layercd src/data-layer && go run src/main.go -d -p 5000
Webcd src/web && npm run dev
Judgecd src/judge && python src/app.py

Postgres and RabbitMQ must already be running.

NextJudge expects 5000, 8080, 5432 and 5672 to be free. Another process on those ports blocks startup.

Terminal window
lsof -i :5000 :8080 :5432 :5672

Stop the conflicting process or change mapped ports in the compose file.

The data layer connects to Postgres using host and port from compose env (default 5432). A Postgres instance already bound on the host often conflicts.

Terminal window
docker logs $(docker ps -qf name=postgres)

Confirm the Postgres container is healthy and that DB_HOST / DB_PASSWORD match compose.

Submissions stay PENDING and the RabbitMQ queue grows when workers are down or cannot authenticate to the API.

Checklist:

  1. Judge container running? docker ps | grep judge
  2. Judge logs: docker logs $(docker ps -qf name=judge) 2>&1 | tail -20
  3. JUDGE_PASSWORD identical on data layer and judge
  4. RabbitMQ credentials match on both sides

Healthy logs show receive → compile → PATCH result. No log activity usually means nothing is consuming the queue.

The initial judge base image build is memory-intensive. Allocate at least 4 GB RAM to Docker (8 GB with web and judge together).