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 is local-only. For internet access, put the web UI behind HTTPS and keep MySQL and the raw API bound to localhost.
Default ports:
| Service | URL |
|---|---|
| Web UI | http://localhost:4000 |
| API | http://localhost:12345 |
| API docs | http://localhost:12345/docs |
| MySQL | localhost:3306 |
The web UI container serves static files through nginx and proxies /api to the API container.
In Docker Compose, MySQL and the API bind to 127.0.0.1 by default. The web UI also binds to 127.0.0.1 unless WEB_BIND_ADDRESS is changed.
- 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 |
API_PORT |
Host port mapped to the API |
WEB_PORT |
Host port mapped to the web UI |
WEB_BIND_ADDRESS |
Host address for the web 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 API browser access |
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.
Use a reverse proxy such as Caddy, nginx, Traefik, or Cloudflare Tunnel in front of the web UI. Terminate HTTPS there.
If the reverse proxy runs on the same host as Benchmarkinator:
WEB_BIND_ADDRESS=127.0.0.1
WEB_PORT=4000
AUTH_COOKIE_SECURE=true
AUTH_COOKIE_SAMESITE=lax
ALLOWED_ORIGINS=https://your-domain.exampleProxy HTTPS traffic to:
http://127.0.0.1:4000
If the reverse proxy runs on another machine and Benchmarkinator runs at 192.168.1.23:
WEB_BIND_ADDRESS=192.168.1.23
WEB_PORT=4000
AUTH_COOKIE_SECURE=true
AUTH_COOKIE_SAMESITE=lax
ALLOWED_ORIGINS=https://your-domain.exampleProxy HTTPS traffic to:
http://192.168.1.23:4000
Recommended network shape:
- expose only the reverse proxy to the internet
- allow access to
WEB_PORTonly from the reverse proxy host - 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.
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 ports
4000,12345, and3306are 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.