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

KeyValue
AUTH_USERyour username, for example admin
AUTH_PASSa long random password — required, the server refuses to start without it
MAX_DB_MBsize cap for live data, for example 2048
EXCLUDEoptional, 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

SettingValue
Container HTTP Port8080
Instance count1 — do not scale CapScope out, each instance would write the same data
HTTPSenabled, plus Force HTTPS
WebSocket Supportoff — the live view uses Server-Sent Events, no WebSocket needed

Persistent directories

Add both rows under Persistent Directories:

Path in containerLabel or host path
/dataa named volume such as capscope-data — the SQLite database lives here
/var/run/docker.sockhost path /var/run/docker.sock
HTTPS is mandatory in production. Do not expose this app on plain HTTP — Basic auth credentials travel in clear text. Mounting 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:

bash
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 dayPer 7 days
1about 35 MBabout 240 MB
10about 350 MBabout 2.4 GB
100about 3.5 GBabout 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

SymptomLikely cause
Exits at start: AUTH_PASS is requiredSet AUTH_PASS, or ALLOW_NO_AUTH=true for a private test.
Exits at start: cannot reach DockerSocket not mounted, or the proxy URL is wrong.
App list emptyINCLUDE or EXCLUDE regex filtering everything, or no containers on this node.
Live view stalls behind a proxyA proxy in front of CapRover is buffering SSE. Disable buffering for /api/logs/stream.
Container shows no logsIts 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 errorsMore 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.