CapRover guide
CapRover needs nothing platform-specific: the repo ships a captain-definition that points at the Dockerfile, so you create the app, set two persistent directories and one instance count, and deploy. This guide also hardens the setup with a read-only socket proxy and sizes the database.
Prerequisites
- CapRover ≥ 1.10 with dashboard access. The CLI is optional.
- Outbound internet on the CapRover host during the first build — Go modules are downloaded then.
- 2–4 GB of RAM available for the build.
- A password for Basic auth:
openssl rand -base64 24.
Create the app
Apps → Create New App
In the CapRover dashboard, create an app named capscope. Leave Has Persistent Data unticked — you add the directories manually in the next step.
Open App Configs
Open the new app and go to the App Configs tab. Everything below is configured there.
Configure it
Environment variables
| Key | Value |
|---|---|
AUTH_USER | your username, for example admin |
AUTH_PASS | a long random password — required, the server refuses to start without it |
MAX_DB_MB | size cap for live data, for example 2048 |
EXCLUDE | optional, for example ^captain- to hide CapRover's own services. Excluded containers are still followed for access logs only: captain-nginx keeps feeding the Access dashboard (parsed requests, no raw log rows) |
The Access tab shows GoAccess-style panels built from captain-nginx's access logs: status codes, top paths, client IPs (anonymized — last octet zeroed — unless ANON_IPS=false), referrers and a requests/errors timeline. Set ACCESS_LOG=off if you do not want it.
App Configs settings
| Setting | Value |
|---|---|
| Container HTTP Port | 8080 |
| Instance count | 1 — do not scale CapScope out, each instance would write the same data |
| HTTPS | enabled, plus Force HTTPS |
| WebSocket Support | off — the live view uses Server-Sent Events, no WebSocket needed |
Persistent directories
Add both rows under Persistent Directories:
| Path in container | Label or host path |
|---|---|
/data | a named volume such as capscope-data — the SQLite database lives here |
/var/run/docker.sock | host path /var/run/docker.sock |
docker.sock is also root equivalent on the host; see the hardening section below for a read-only alternative.CapRover's nginx does not buffer the live stream: CapScope sends X-Accel-Buffering: no so SSE flows through as-is.
Deploy the code
Either push from your checkout with the CapRover CLI:
npm i -g caprover
caprover deploy
Pick your server and the capscope app. CapRover reads captain-definition, which points at the Dockerfile and builds a static scratch image. Alternatively zip the project (without .git) and upload it in the app's Deployment tab, or upload a release tarball there. The first build downloads Go modules and may take several minutes.
Verify
Open and log in
Visit https://capscope.<root-domain> and log in with the Basic auth credentials.
Check the container count
The header chip should show a container count above zero within about 10 seconds.
Probe the health endpoint
curl -k https://capscope.<domain>/healthz returns 200 — the path is auth-free by design.
Trigger a test crash
Run a throwaway container that exits with code 1 and check the Crashes tab for the snapshot.
Hardening: socket proxy (recommended)
Mounting docker.sock gives the container host root access. Run a socket proxy that exposes only read endpoints, and point CapScope at it:
Create the proxy app
Create an app docker-proxy from the image tecnativa/docker-socket-proxy. Bind-mount /var/run/docker.sock read only and set environment CONTAINERS=1, EVENTS=1, PING=1, POST=0. Do not map any public port and keep Expose as web app off.
Repoint CapScope
On the capscope app, remove the /var/run/docker.sock mount and set DOCKER_HOST=tcp://srv-captain--docker-proxy:2375, then redeploy.
Confirm the proxy allows the calls
CONTAINERS=1 is what allows GET /containers/* (inspect, logs, stats). Proxy defaults vary between image versions, so verify on your CapRover version that CapScope still starts.
Capacity planning
Measured on a sandbox: about 400 bytes of database per stored log line for a 130-byte JSON line (three indexes included). Estimate with bytes per day = lines per second × 86400 × 400:
| Lines per second (all apps) | Per day | Per 7 days |
|---|---|---|
1 | about 35 MB | about 240 MB |
10 | about 350 MB | about 2.4 GB |
100 | about 3.5 GB | about 24 GB |
MAX_DB_MB wins over LOG_RETENTION. If your volume is high, the oldest logs are dropped early to stay under the cap. Raise the cap, lower the retention, or use EXCLUDE for chatty containers such as captain-nginx. Metrics are cheap: one row per container every 10 seconds is about 8,600 rows per container per day.Troubleshooting
| Symptom | Likely cause |
|---|---|
Exits at start: AUTH_PASS is required | Set AUTH_PASS, or ALLOW_NO_AUTH=true for a private test. |
Exits at start: cannot reach Docker | Socket not mounted, or the proxy URL is wrong. |
| App list empty | INCLUDE or EXCLUDE regex filtering everything, or no containers on this node. |
| Live view stalls behind a proxy | A proxy in front of CapRover is buffering SSE. Disable buffering for /api/logs/stream. |
| Container shows no logs | Its logging driver does not support reading (docker logs fails for it). |
| Data gone after redeploy | /data is not on a persistent volume. |
| Corrupt DB / locked errors | More than one replica — scale back to 1. |
Single-node CapRover is fine: CapScope only sees containers on the node its task lands on. Multi-node agent mode is on the roadmap.