Built specifically to power live code execution for zachcodinginterview - the AI-powered mock interview platform. Every coding interview and practice submission on that platform is run through this service.
This is the code execution sandbox for zachcodinginterview. Whenever a candidate submits code during a coding interview or a practice session, the main app sends that code here, this service runs it as a resource-limited subprocess, and returns stdout, stderr, the exit code, and whether it timed out - which the main app then compares against expected test case output to decide pass/fail.
It intentionally does not rely on Docker-in-Docker or privileged containers for isolation. Instead, it uses plain OS-level resource limits (CPU time, memory, process count, open files) on a subprocess. That's weaker isolation than something like Piston, but it's deployable literally anywhere that can run Python - a normal VPS, a free-tier PaaS container, or even directly inside Termux on an Android phone.
- Two runtimes: Python 3 and Node.js (JavaScript), selected per-request
- Resource-limited execution - CPU time, wall-clock time, memory, process count, and open-file limits, all independently tunable via env vars
- Clean, minimal environment - no host secrets or env vars leak into candidate code; a small
UV_THREADPOOL_SIZEkeeps Node's thread pool tight - Output truncation - stdout/stderr capped so a runaway
printloop can't blow up the response - API key protected - every
/executecall requires anX-API-Keyheader, and the server fails closed if no key is configured - Health check endpoint for uptime monitoring
- No special container privileges required - runs fine under restrictive PaaS sandboxes (with the one exception noted below) and even under Termux
| Endpoint | Method | Description |
|---|---|---|
/execute |
POST |
Runs a code snippet (language, code, stdin) and returns stdout, stderr, exit_code, timed_out, error. Requires X-API-Key header. |
/health |
GET |
Returns server status and which languages are currently supported. |
POST /execute request body:
{
"language": "python",
"code": "print(input())",
"stdin": "hello"
}Response:
{
"stdout": "hello\n",
"stderr": "",
"exit_code": 0,
"timed_out": false,
"error": null
}sequenceDiagram
participant Candidate
participant App as zachcodinginterview<br/>(Next.js + tRPC)
participant Code as codeExecution.ts<br/>+ testHarness.ts
participant Server as Code Server<br/>(this repo)
participant Runner as Subprocess Sandbox<br/>(Python / Node)
Candidate->>App: Submit code for a test case
App->>Code: runAgainstTestCases()
Code->>Code: wrapWithHarness()
Code->>Server: POST /execute (X-API-Key)
Server->>Runner: spawn resource-limited subprocess
Runner-->>Server: stdout / stderr / exit code
Server-->>Code: ExecuteResponse
Code->>Code: compare actual vs expected output
Code-->>App: pass / fail / error / timeout
App-->>Candidate: test case result
| Layer | Technology |
|---|---|
| Framework | FastAPI |
| Server | Uvicorn (ASGI) |
| Language | Python 3.12 |
| Executable runtimes | Python 3, Node.js 20.x |
| Isolation | OS-level resource limits (CPU, memory, processes, open files) |
| Config | python-dotenv |
| Containerization | Docker (Debian Bullseye slim base) |
git clone https://github.com/19akshansh/zachcodinginterview_codeserver.git
cd zachcodinginterview_codeserverCopy the example file and set your own API key:
cp example.env .envGenerate a strong API key:
python3 -c "import secrets; print(secrets.token_hex(32))"pip install -r requirements.txt
uvicorn app:app --host 0.0.0.0 --port 8000docker build -t zachcodinginterview-codeserver .
docker run -p 8000:8000 --env-file .env zachcodinginterview-codeserverThen point your zachcodinginterview deployment's CODESERVER_API_URL at wherever this service ends up running, with CODESERVER_APIKEY matching the API_KEY you set above.
| Variable | Description | Default |
|---|---|---|
API_KEY |
Required secret checked against the X-API-Key header on /execute. Server refuses to run /execute at all if this isn't set. |
- |
CPU_TIME_LIMIT_SECONDS |
Max CPU time per execution | 3 |
WALL_TIME_LIMIT_SECONDS |
Max wall-clock time per execution | 5 |
MEMORY_LIMIT_MB |
Max memory per execution (Python only - see limitations) | 200 |
MAX_PROCESSES |
Max number of processes/threads the sandboxed code can spawn | 64 (20 in example.env) |
MAX_OPEN_FILES |
Max open file descriptors | 64 |
MAX_OUTPUT_BYTES |
Max combined stdout/stderr size before truncation | 100000 |
This is a lightweight subprocess sandbox, not a hardened, container-per-execution sandbox like Piston or Judge0. Keep this in mind before relying on it for anonymous, unproctored, fully adversarial users:
- Isolation is enforced via
resourcelimits and a clean environment, not per-execution containers or VMs. - Memory limits (
RLIMIT_AS) are only applied to Python. Node/V8 reserves a large chunk of virtual address space at startup regardless of actual heap usage, so a hardRLIMIT_ASwould kill the process before it runs - Node's memory is instead bounded with--max-old-space-sizeon the command itself. - Runs through Termux on my phone so might have bad uptime ;-;
zachcodinginterview_codeserver/
├── app.py # FastAPI app - /execute and /health
├── requirements.txt # fastapi, uvicorn, pydantic, python-dotenv
├── Dockerfile # Debian Bullseye slim + Python 3.12 + Node.js 20.x
├── example.env # copy to .env and fill in your API key + limits
└── .gitignore
Contributions are welcome.
- Fork the repository
- Create a new branch (
git checkout -b feat/my-feature) - Commit your changes
- Open a pull request
Licensed under the MIT License, same as the main zachcodinginterview project.