Prometheus Metrics¶
CashPilot exposes rich Prometheus metrics when enabled, allowing you to monitor your passive income fleet with Grafana, Alertmanager, or any Prometheus-compatible tool.
Enabling Metrics¶
Set the environment variable on the UI container:
Metrics are exposed at GET /metrics (unauthenticated, per Prometheus convention).
Security: The
/metricsendpoint exposes operational data (earnings balances, worker hostnames, container status). Keep it accessible only from trusted networks (LAN, Tailscale, VPN) or protect it behind a reverse proxy with IP allowlist/authentication.
Scrape Configuration¶
Add to your prometheus.yml:
scrape_configs:
- job_name: cashpilot
scrape_interval: 60s
static_configs:
- targets: ["cashpilot-ui:8080"]
Available Metrics¶
System¶
| Metric | Type | Labels | Description |
|---|---|---|---|
cashpilot_uptime_seconds |
Gauge | -- | Seconds since CashPilot process started |
cashpilot_info |
Info | version, title |
Build information |
HTTP¶
| Metric | Type | Labels | Description |
|---|---|---|---|
cashpilot_http_requests_total |
Counter | method, path, status |
Total HTTP requests |
cashpilot_http_request_duration_seconds |
Histogram | method, path |
Request latency (buckets: 5ms to 10s) |
cashpilot_http_requests_in_progress |
Gauge | method |
Currently active requests |
Containers¶
| Metric | Type | Labels | Description |
|---|---|---|---|
cashpilot_containers_total |
Gauge | status, node |
Container count by status and worker node |
cashpilot_container_info |
Gauge | service, node, status, image |
Container presence (1 = exists) |
cashpilot_container_cpu_percent |
Gauge | service, node |
CPU usage per container |
cashpilot_container_memory_mb |
Gauge | service, node |
Memory usage in MB per container |
cashpilot_container_lifecycle_total |
Counter | action, service |
Lifecycle events (deploy/stop/restart/remove) |
Earnings¶
| Metric | Type | Labels | Description |
|---|---|---|---|
cashpilot_earnings_balance |
Gauge | platform, currency |
Latest balance per platform (native currency) |
cashpilot_earnings_balance_usd |
Gauge | platform |
Latest balance per platform (USD) |
cashpilot_earnings_total_usd |
Gauge | -- | Sum of all platform balances in USD |
Collection¶
| Metric | Type | Labels | Description |
|---|---|---|---|
cashpilot_collection_runs_total |
Counter | result |
Collection runs (success/error) |
cashpilot_collection_duration_seconds |
Histogram | -- | Duration of collection runs (buckets: 1s to 5min) |
cashpilot_collection_errors_total |
Counter | platform |
Per-platform collection errors |
cashpilot_collection_last_success_timestamp |
Gauge | -- | Unix timestamp of last successful run |
cashpilot_collection_platforms_scraped |
Gauge | -- | Platforms successfully scraped in last run |
cashpilot_collection_collectors_configured |
Gauge | -- | Collectors the last run attempted |
cashpilot_notify_delivery_total |
Counter | result |
Out-of-band alert deliveries (success/error) |
cashpilot_notify_last_success_timestamp |
Gauge | -- | Unix timestamp of the last accepted delivery |
Workers¶
| Metric | Type | Labels | Description |
|---|---|---|---|
cashpilot_workers_total |
Gauge | status |
Workers by status (online/offline) |
cashpilot_worker_last_heartbeat_seconds |
Gauge | worker |
Seconds since last heartbeat |
cashpilot_worker_docker_available |
Gauge | worker |
Docker availability (1=yes, 0=no) |
cashpilot_worker_containers_count |
Gauge | worker |
Number of containers per worker |
cashpilot_heartbeats_total |
Counter | worker |
Total heartbeats received |
Health¶
| Metric | Type | Labels | Description |
|---|---|---|---|
cashpilot_health_score |
Gauge | service |
Health score per service (0-100) |
cashpilot_health_uptime_percent |
Gauge | service |
Uptime percentage over last 7 days |
cashpilot_services_deployed_total |
Gauge | -- | Number of deployed services |
cashpilot_services_available_total |
Gauge | -- | Number of services in catalog |
Auth¶
| Metric | Type | Labels | Description |
|---|---|---|---|
cashpilot_login_attempts_total |
Counter | result |
Login attempts (success/failure) |
cashpilot_login_rate_limited_total |
Counter | -- | Rate-limited login attempts |
Example Alerts¶
Every rule below is computed from CashPilot's own metrics, so the whole
section requires CASHPILOT_METRICS_ENABLED=true (it is off by default —
see Enabling Metrics). Without it none of these alerts
exist, including CashPilotDown.
groups:
- name: cashpilot
rules:
- alert: WorkerOffline
expr: cashpilot_worker_last_heartbeat_seconds > 300
for: 5m
labels:
severity: warning
annotations:
summary: "Worker {{ $labels.worker }} has not sent a heartbeat in 5+ minutes"
- alert: CollectionFailing
expr: increase(cashpilot_collection_runs_total{result="error"}[1h]) > 3
labels:
severity: warning
annotations:
summary: "Earnings collection has failed 3+ times in the last hour"
- alert: ServiceDown
expr: cashpilot_health_score < 50
for: 15m
labels:
severity: critical
annotations:
summary: "Service {{ $labels.service }} health score below 50"
- alert: EarningsStale
expr: time() - cashpilot_collection_last_success_timestamp > 7200
labels:
severity: warning
annotations:
summary: "No successful earnings collection in 2+ hours"
# A run where EVERY collector fails is recorded as result="error" and
# does NOT refresh the success timestamp, so the two rules above fire on
# a total outage. This one additionally catches the same state directly.
- alert: NothingCollected
expr: cashpilot_collection_platforms_scraped == 0 and cashpilot_collection_collectors_configured > 0
for: 3h
labels:
severity: warning
annotations:
summary: "Collection runs are completing but zero platforms produced a reading"
# The alert channel itself needs watching: a delivery path that is down
# sends nothing, and that silence reads exactly like health.
- alert: AlertDeliveryFailing
expr: increase(cashpilot_notify_delivery_total{result="error"}[6h]) > 0
labels:
severity: warning
annotations:
summary: "Out-of-band alert deliveries are failing — pushes are not reaching you"
# THE ONE THAT WATCHES THE WATCHER. Every alert above is computed FROM
# CashPilot's own metrics, so if CashPilot itself stops, none of them
# fire -- there is simply no data, and silence reads exactly like health.
# This is the only rule here that survives the thing it monitors dying.
- alert: CashPilotDown
expr: up{job="cashpilot"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Prometheus cannot scrape CashPilot — every other CashPilot alert is now blind"
The container can be "up" and failing¶
Both images declare a HEALTHCHECK, so Docker knows whether the app inside is
actually answering:
Nothing acts on that by default, and that surprises people.
restart: unless-stopped restarts a container that exits. A container that
stays running while failing its own healthcheck is not exiting, so it is never
restarted — it sits there, unhealthy, indefinitely. (Docker Swarm does
reschedule unhealthy tasks; plain Docker and Compose do not.)
Three ways to close that, in increasing order of effort:
1. Alert on it. If you already run Prometheus, the CashPilotDown rule
above covers the case that matters: a failing healthcheck almost always means
the HTTP endpoint stopped answering, which is the same thing a failed scrape
detects.
2. Restart it automatically. A small sidecar watches the Docker socket and restarts anything that goes unhealthy:
autoheal:
image: willfarrell/autoheal:1.2.0
restart: unless-stopped
environment:
- AUTOHEAL_CONTAINER_LABEL=autoheal
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Then label the containers you want it to manage with autoheal: "true".
This sidecar holds the Docker socket
The socket is root on the host. You are trading one risk for another, and on a single-user home server that is usually a reasonable trade — but make it deliberately, not by copy-paste. If you would not give a third-party image root, alert instead of autohealing.
3. Watch it by hand. docker ps shows the health state, and it costs
nothing to look at after an upgrade.
Grafana Dashboard¶
Import a basic dashboard by querying these panels:
- Total Earnings (USD):
cashpilot_earnings_total_usd - Per-Platform Earnings:
cashpilot_earnings_balance_usd - Container Health Heatmap:
cashpilot_health_score - Worker Status:
cashpilot_workers_total - Collection Success Rate:
rate(cashpilot_collection_runs_total{result="success"}[1h]) - HTTP Latency p95:
histogram_quantile(0.95, rate(cashpilot_http_request_duration_seconds_bucket[5m]))