Benchmarkinator is a local benchmark result manager for hardware test systems.
It stores:
- hardware components
- complete test system configurations
- benchmark definitions
- benchmark results
It provides:
- a FastAPI backend
- a React/Vite web UI
- a MySQL database through Docker Compose
- optional hardware seed data
- backup and restore scripts
Default configuration exposes the admin and public web UIs on the host network. For internet access, put the public UI behind HTTPS, keep the admin UI private, and keep MySQL and the raw API bound to localhost.
Default ports:
| Service | URL |
|---|---|
| Admin UI | http://:8001 |
| Public UI | http://:8002 |
| API | http://localhost:12345 |
| API docs | http://localhost:12345/docs |
| MySQL | localhost:3306 |
The admin and public UI containers serve static files through nginx. The admin UI proxies /api to the API container. The public UI only proxies /api/public/*.
In Docker Compose, MySQL and the raw API bind to 127.0.0.1 by default. The admin and public web UIs bind to 0.0.0.0 by default so they can be reached from the host network or a reverse proxy.
Public UI routes:
| Route | Purpose |
|---|---|
/ |
Public benchmark dashboard |
/benchmarks |
Public benchmark catalog and leaderboard list |
/benchmarks/:id |
Dedicated benchmark leaderboard |
/hardware |
Public CPU/GPU hardware catalog |
/hardware/:type/:id |
Dedicated CPU/GPU result page |
/results |
Searchable public benchmark results |
/systems |
Searchable public test-system catalog |
/systems/:id |
Dedicated public test-system profile and comparison view |
- Docker
- Docker Compose
- Bash for
setup.sh,backup.sh, andrestore.sh
For local development outside Docker:
- Python 3.13
- Node.js 22+
- npm
Copy the example environment file:
cp env.example .envEdit .env as needed.
Start the application:
docker compose build
docker compose up -dOr run the setup script:
./setup.shsetup.sh creates .env if needed, generates credentials, asks whether to load seed hardware data, optionally configures a backup cron job, builds the containers, and can start the stack.
Important .env values:
| Variable | Purpose |
|---|---|
MYSQL_DATABASE |
MySQL database name |
MYSQL_USER |
MySQL application user |
MYSQL_PASSWORD |
MySQL application user password |
MYSQL_ROOT_PASSWORD |
MySQL root password |
MYSQL_PORT |
Host port mapped to MySQL |
MYSQL_POOL_RECYCLE_SECONDS |
API MySQL connection recycle interval; helps avoid stale idle connections |
API_PORT |
Host port mapped to the API |
ADMIN_PORT |
Host port mapped to the admin UI |
ADMIN_BIND_ADDRESS |
Host address for the admin UI binding |
PUBLIC_PORT |
Host port mapped to the public UI |
PUBLIC_BIND_ADDRESS |
Host address for the public UI binding |
API_KEY |
API key for protected API routes |
WEBADMIN |
Web login username |
WEBPASSWORD |
Web login password |
AUTH_TOKEN_TTL_SECONDS |
Lifetime of web login tokens |
AUTH_COOKIE_SECURE |
Set to true when the web UI is served over HTTPS |
AUTH_COOKIE_SAMESITE |
Browser SameSite policy for the session cookie |
LOGIN_RATE_LIMIT_ATTEMPTS |
Failed login attempts allowed per window |
LOGIN_RATE_LIMIT_WINDOW_SECONDS |
Login rate-limit window |
ALLOWED_ORIGINS |
Comma-separated CORS allowlist for direct browser calls to the raw API; usually blank when using the admin/public UI containers |
PUBLIC_RESULTS_CACHE_SECONDS |
Backend cache TTL for public results data |
LOAD_HARDWARE_DATA |
Whether to load seed hardware data on startup |
HARDWARE_ERA |
Seed data set: retro, retroextended, or modern |
The API also accepts DATABASE_URL; Docker Compose sets it automatically.
The web UI logs in through the backend using WEBADMIN and WEBPASSWORD.
Successful login sets a signed, HttpOnly session cookie. The protected API routes accept either:
- the configured raw
API_KEY - a valid signed web token
The raw API_KEY is for trusted scripts or local administration. Do not publish it in frontend code or share it with users.
Benchmark results can be viewed without logging in through the public results route. Editing data requires login.
The public container rate-limits /api/public/* and caches /api/public/results-data at nginx for 30 seconds. The API also keeps a short in-process cache controlled by PUBLIC_RESULTS_CACHE_SECONDS so public traffic does not query MySQL on every request.
Use a reverse proxy such as Caddy, nginx, Traefik, or Cloudflare Tunnel in front of the public UI. Terminate HTTPS there. Keep the admin UI private.
ALLOWED_ORIGINS is only for browser calls made directly to the raw API port from a different origin. When users access the admin or public UI containers through HAProxy and those containers proxy /api, leave ALLOWED_ORIGINS blank.
If the reverse proxy runs on the same host as Benchmarkinator:
ADMIN_BIND_ADDRESS=127.0.0.1
ADMIN_PORT=8001
PUBLIC_BIND_ADDRESS=127.0.0.1
PUBLIC_PORT=8002
AUTH_COOKIE_SECURE=true
AUTH_COOKIE_SAMESITE=lax
ALLOWED_ORIGINS=Proxy HTTPS traffic to:
http://127.0.0.1:8002
If the reverse proxy runs on another machine and Benchmarkinator runs at 192.168.1.23:
ADMIN_BIND_ADDRESS=127.0.0.1
ADMIN_PORT=8001
PUBLIC_BIND_ADDRESS=192.168.1.23
PUBLIC_PORT=8002
AUTH_COOKIE_SECURE=true
AUTH_COOKIE_SAMESITE=lax
ALLOWED_ORIGINS=Proxy HTTPS traffic to:
http://192.168.1.23:8002
Recommended network shape:
- expose only the reverse proxy to the internet
- allow access to
PUBLIC_PORTonly from the reverse proxy host or intended public network - keep
ADMIN_PORTprivate - keep MySQL on
127.0.0.1:${MYSQL_PORT} - keep the direct API on
127.0.0.1:${API_PORT} - use long random values for
API_KEY,MYSQL_PASSWORD,MYSQL_ROOT_PASSWORD, andWEBPASSWORD - keep
.env,backups/, and database ports out of public web roots
This is reasonable for a small private service if you use HTTPS and strong credentials. It is not a full multi-user SaaS security model: there is one admin login, no MFA, no password reset flow, and no account-level permissions.
Seed SQL files are in extras/sql:
retro_hardware_2005.sqlretro_hardware_extended_2008.sqlmodern_hardware.sql
Set these in .env before startup:
LOAD_HARDWARE_DATA=true
HARDWARE_ERA=retroextendedThe loader is intended for initial/local data population. Review the SQL files before using them with an existing database.
For UI testing, use the demo seeder to create a larger read-only browsing dataset with demo hardware, test systems, benchmarks, and benchmark results:
docker compose exec benchmarkinator-api python scripts/seed_demo_data.py --reset-demoFor local development outside Docker:
.venv/bin/python scripts/seed_demo_data.py --reset-demoThe demo seeder creates deterministic data, including mixed-era systems and benchmark compatibility gaps. It does not create a global score. Rankings are still per benchmark.
Safety flags:
--reset-demoremoves existing demo systems/results and reseeds them.--appendallows seeding when real non-demo data already exists.- With neither flag, the script refuses to run on a non-empty database.
Alembic is included.
Fresh database:
alembic upgrade headExisting database that already matches the current models:
alembic stamp headExisting database that has tables but no alembic_version row:
alembic stamp 46cd05edcf1b
alembic upgrade headUse this when Alembic tries to create tables that already exist. The first command records the existing baseline schema; the second command applies later migrations.
The app still creates missing tables on startup for local convenience. Use Alembic for schema changes.
Backup:
./backup.shRestore:
./restore.shBackups are written under backups/.
Review these scripts before running them against data you care about.
Read CPUs:
curl -H "X-API-Key: YOUR_KEY" http://localhost:12345/api/cpu/Add a result:
curl -X POST \
-H "X-API-Key: YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"benchmark_id": 1, "config_id": 1, "result": 1500}' \
http://localhost:12345/api/benchmark_results/Public results data:
curl http://localhost:12345/api/public/results-dataBackend:
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt -r requirements-dev.txt
uvicorn main:app --reload --host 0.0.0.0 --port 12345Frontend:
cd webui
npm install
npm run devThe Vite dev server runs on port 4000.
Backend tests:
pytestFrontend production build:
cd webui
npm run buildCheck containers:
docker compose psView logs:
docker compose logs -fRebuild after dependency or frontend changes:
docker compose build
docker compose up -dCommon checks:
- Make sure
ADMIN_PORT,PUBLIC_PORT,API_PORT, andMYSQL_PORTare available or change them in.env. - Make sure
.envexists. - If login fails, check
WEBADMIN,WEBPASSWORD, andAPI_KEY. - If the API cannot connect to MySQL, check
MYSQL_*values anddocker compose logs benchmarkinator-db.
MIT. See LICENSE.