Install with Docker (recommended)
The supported way to run Holdout Lab is the Docker Compose bundle from holdoutlab.com/download: three commands, no toolchain, everything on your machine. (Developers who want to hack on the source use the from-source install instead.)
Requirements
Section titled “Requirements”- macOS — Docker Desktop for Mac; Apple Silicon runs natively.
- Windows 10/11 — Docker Desktop for Windows with its default WSL 2 backend (Docker Desktop sets this up itself — no extra steps).
- Linux — Docker Desktop, or plain
Docker Engine plus the
compose plugin (every command below uses
docker compose, the v2 plugin — not the olddocker-composebinary). On Engine, add your user to thedockergroup or prefix the commands withsudo. - ~2 GB free disk to start; market data grows with what you ingest.
Install Docker and open it once so the daemon is running before you continue.
Verify the download (optional)
Section titled “Verify the download (optional)”Download SHA256SUMS into the same folder as the zip, then:
shasum -a 256 -c SHA256SUMS --ignore-missing # macOSsha256sum -c SHA256SUMS --ignore-missing # LinuxOn Windows (PowerShell): Get-FileHash .\holdout-lab-vX.Y.Z.zip and compare the hash against the
matching line in SHA256SUMS.
Install
Section titled “Install”Download and unzip the bundle, then open a terminal in the unzipped folder.
macOS / Linux (also WSL or Git Bash on Windows):
./setup.sh # once: creates .env with a fresh session secretdocker compose up -d # pulls the images and starts the stackWindows (PowerShell) — setup.sh is a shell script, so create .env with these lines
instead (they do exactly what the script does: copy .env.template to .env with a fresh
random session secret), then start the stack:
$bytes = [byte[]]::new(32)[System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)$secret = -join ($bytes | ForEach-Object { $_.ToString('x2') })(Get-Content .env.template) -replace '__GENERATE__', $secret | Set-Content .envdocker compose up -dOpen http://localhost:8080 and register — the first account becomes the owner and
registration closes after it. Everything binds to 127.0.0.1; nothing is reachable from other
machines, and nothing phones home.
The stack: web (the UI, the only published port), api, worker, postgres, redis — plus an
optional IB gateway (below). Your database, market data, artifacts, and saved keys live on named
Docker volumes, so they survive restarts and updates.
First steps
Section titled “First steps”Continue with First run — register, add keys in Settings, ingest data, run the gauntlet. Two container-edition notes:
- Zero keys needed to start: the Getting started checklist (Learn tab) can ingest the crypto starter basket from free public data, and crypto paper trading runs against live Kraken market data with no exchange account.
- Keys you save in Settings are stored on the shared
configvolume — both the api and the worker read them, and they survive image updates. After saving a data key, use the in-app Restart worker button as prompted (in this edition it triggers a clean self-restart via compose).
IB paper trading (advanced, optional)
Section titled “IB paper trading (advanced, optional)”The bundle includes an IB Gateway service (profile ib) so the app can paper-trade your own
Interactive Brokers account:
-
Put your IBKR credentials in the bundle’s
.env(uncommentTWS_USERID/TWS_PASSWORD). The gateway’s headless login can’t complete app-based 2FA — see paper trading for account setup options. -
Start it alongside the app:
Terminal window docker compose --profile ib up -d -
Enter your paper account id (
DU…) in Settings → Interactive Brokers.
The app reaches the gateway inside the compose network (service name ib-gateway-paper); its
API port is never published to the host. Gateways are compose-managed in this edition — the in-app
gateway buttons show you the matching docker compose commands instead of starting containers
themselves. To watch the gateway GUI (rescue an unexpected dialog), set VNC_SERVER_PASSWORD in
.env and VNC to localhost:5900.
Updating
Section titled “Updating”- Download the latest bundle from holdoutlab.com/download and
replace
docker-compose.yml(keep your.env). -
Terminal window docker compose pulldocker compose up -d
Volumes are untouched: login, keys, data, and runs all persist. The running version shows in Settings → System.
Backup & restore
Section titled “Backup & restore”Back up the database (runs, sessions, settings) any time:
docker compose exec -T postgres pg_dump -U ats ats > holdout-backup.sqlRestore into a fresh stack:
docker compose exec -T postgres psql -U ats ats < holdout-backup.sqlMarket data isn’t in the dump — it re-ingests from the vendors on demand. To snapshot everything
including market data, back up the Docker volumes (pgdata, appdata, artifacts, config).
Troubleshooting
Section titled “Troubleshooting”docker compose ps— every service should berunning(apiand the gateway report health).docker compose logs api --tail 50(orworker,web) — startup errors land here. A missing.envmeans the setup step hasn’t been run (./setup.sh, or the PowerShell lines above on Windows).- The UI is served at
http://localhost:8080only —:5173and:8000belong to the developer install.
Uninstall
Section titled “Uninstall”docker compose down # stop (data volumes are kept)docker compose down -v # stop AND delete all data — irreversibleRelated
Section titled “Related”- First run — account, keys, first backtest, the gauntlet.
- Container edition reference — how this edition differs from a source install, and running the same stack from a checkout.
- Configuration reference — every setting.