Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ There is no test suite. After changing `docker-compose.yml`, always run `docker

**Network isolation matters.** Services are split across four bridge networks and one host-mode service. A service can only reach another if they share a network — adding a new service requires picking the right one (or declaring multiple):

- `monitoring_network` — tautulli, grafana, telegraf, watchtower, portainer
- `monitoring_network` — tautulli, grafana, telegraf, watchtower, portainer, prometheus, cadvisor, node-exporter
- `media_network` — seerr, radarr, sonarr, prowlarr, bazarr
- `download_network` — transmission, watchlistarr, cleanarr, requestrr, radarr, sonarr
- `tracearr-network` — tracearr, timescale (PostgreSQL), redis
Expand All @@ -37,7 +37,9 @@ Radarr and Sonarr are deliberately on both `media_network` (so Seerr, Prowlarr,

**Volume paths are intentionally user-specific.** All bind mounts are rooted at `${USERDIR}` from `.env`. When advising the user, do not assume any particular host path layout — the README explicitly tells them to update paths to match their drive mounts.

**No Prometheus in the stack.** There is no `prometheus` service in `docker-compose.yml`. A starting-point config lives at `docs/prometheus.example.yml` for users who want to add Prometheus themselves — don't assume metrics are being scraped today.
**Prometheus is live, but only cAdvisor + node-exporter feed it.** `prometheus/prometheus.yml` is mounted into the prometheus container; only the `cadvisor` and `node_exporter` scrape jobs are active. The `telegraf` and `tautulli` jobs are commented out because those containers don't expose a `/metrics` endpoint by default — telegraf would need the `prometheus_client` output plugin enabled, and Tautulli needs a metrics plugin installed. Grafana points at Prometheus the same way it would point at any data source — configure it in the Grafana UI after first boot.

**cAdvisor needs `privileged: true` and several host-fs mounts.** This is the standard cAdvisor pattern; flag it if a user reports security concerns about the monitoring stack. It also binds host port `8080` — if a user has something else on `8080`, that's the conflict.

**Transmission uses `haugene/transmission-openvpn` and won't start without VPN credentials.** The container runs an OpenVPN client internally; `OPENVPN_PROVIDER`, `OPENVPN_CONFIG`, `OPENVPN_USERNAME`, and `OPENVPN_PASSWORD` must all be set in `.env`. The compose service declares `cap_add: NET_ADMIN` and `devices: /dev/net/tun` for the OpenVPN client; the data volume is `/data` (haugene's convention), not `/config` like the linuxserver image. `LOCAL_NETWORK` (CIDR, default `192.168.0.0/16`) controls which destinations bypass the tunnel — if a user reports the web UI is unreachable, this is almost always the cause.

Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ flowchart LR

Telegraf -->|host + container metrics| Grafana
Tautulli -->|usage data| Grafana

cAdvisor -->|container metrics| Prometheus
NodeExporter[node-exporter] -->|host metrics| Prometheus
Prometheus -->|scraped time-series| Grafana
```

See [Network Architecture](#network-architecture) below for the exact network membership of each service.
Expand Down Expand Up @@ -143,6 +147,9 @@ A ready-to-use [Kometa](https://kometa.wiki/) (Plex Meta Manager) configuration
|---------|-------------|------|
| [Tautulli](https://tautulli.com/) | Plex usage monitoring | `8181` |
| [Grafana](https://grafana.com/) | Metrics visualization | `3000` |
| [Prometheus](https://prometheus.io/) | Time-series metrics database — scrapes cAdvisor + node-exporter | `9090` |
| [cAdvisor](https://github.com/google/cadvisor) | Per-container CPU / memory / network metrics | `8080` |
| [node-exporter](https://github.com/prometheus/node_exporter) | Host (CPU / disk / network) metrics | `9100` |
| [Telegraf](https://www.influxdata.com/time-series-platform/telegraf/) | Metrics collection agent | N/A |
| [Tracearr](https://github.com/connorgallopo/tracearr) | Stream tracking and account sharing detection | `3001` |
| [Portainer](https://www.portainer.io/) | Docker management UI ([note on socket access](#a-note-on-portainer)) | `9000` |
Expand All @@ -166,7 +173,7 @@ These services pair well with this stack but are not included in the default `do

Services are isolated into separate Docker networks:

- **`monitoring_network`** - Tautulli, Grafana, Telegraf, Watchtower, Portainer
- **`monitoring_network`** - Tautulli, Grafana, Telegraf, Watchtower, Portainer, Prometheus, cAdvisor, node-exporter
- **`media_network`** - Seerr, Radarr, Sonarr, Prowlarr, Bazarr
- **`download_network`** - Transmission, Watchlistarr, Cleanarr, Requestrr, Radarr, Sonarr
- **`tracearr-network`** - Tracearr, TimescaleDB, Redis
Expand Down
57 changes: 57 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,62 @@ services:
- portainer_data:/data
restart: unless-stopped

prometheus:
container_name: prometheus
image: prom/prometheus:latest
networks:
- monitoring_network
ports:
- "9090:9090"
environment:
- TZ=${TZ}
command:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use ${USERDIR}-rooted bind path for Prometheus config mount.

This path breaks the repo’s bind-mount convention and user-specific layout contract.

Suggested diff
-      - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
+      - ${USERDIR}/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro

As per coding guidelines: docker-compose.yml: All bind mount volume paths must be rooted at ${USERDIR} from .env, not hardcoded host paths, to respect user-specific storage layouts.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
- ${USERDIR}/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` at line 99, Replace the hardcoded host bind
"./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro" in the
docker-compose volume entry with a ${USERDIR}-rooted path from the .env file
(e.g., "${USERDIR}/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro")
so the Prometheus config mount follows the repo convention; update the volume
line in docker-compose.yml where the Prometheus bind is defined to reference
${USERDIR} instead of a relative ./ path.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skipping. prometheus/prometheus.yml is committed to the repo and is the same for every user — it is not user-specific state. The relative path ./prometheus/prometheus.yml resolves against the compose project directory (the repo root, where users run docker compose), so it works out of the box. Switching to ${USERDIR}/prometheus/prometheus.yml would force every user to copy the file into their USERDIR before docker compose up would succeed, which the README does not document. The CLAUDE.md ${USERDIR} rule is about user data bind mounts (/config, /media, etc.), not committed config-as-code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshdev8, that's a well-reasoned distinction — you're right. The ${USERDIR} convention is meant for user-specific runtime state (config dirs, media libraries, data directories, etc.), not for config-as-code files that are version-controlled in the repo itself. ./prometheus/prometheus.yml is the correct approach here since it resolves relative to the compose project root and works for all users without any extra setup. I'll update my understanding for future reviews.


✏️ Learnings added
Learnt from: joshdev8
Repo: joshdev8/AutoPlexx PR: 39
File: docker-compose.yml:99-99
Timestamp: 2026-05-13T18:25:49.929Z
Learning: In the `joshdev8/AutoPlexx` repo (`docker-compose.yml`), the `${USERDIR}` bind-mount convention applies only to **user-specific runtime state** (e.g., `/config`, `/media`, `/data` directories). Committed config-as-code files (e.g., `prometheus/prometheus.yml` checked into the repo) should use relative paths like `./prometheus/prometheus.yml`, which resolve from the compose project root. Do NOT flag relative repo-root paths as violations of the `${USERDIR}` rule.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

- prometheus_data:/prometheus
restart: unless-stopped

# Container resource metrics for Prometheus. Reads the Docker socket
# read-only and several host filesystems to enumerate running containers.
cadvisor:
container_name: cadvisor
image: gcr.io/cadvisor/cadvisor:latest
networks:
- monitoring_network
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
- /dev/disk/:/dev/disk:ro
devices:
- /dev/kmsg
privileged: true
restart: unless-stopped

# Host (kernel / disk / network) metrics for Prometheus.
node-exporter:
container_name: node-exporter
image: prom/node-exporter:latest
networks:
- monitoring_network
ports:
- "9100:9100"
command:
- "--path.procfs=/host/proc"
- "--path.sysfs=/host/sys"
- "--path.rootfs=/rootfs"
- "--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
restart: unless-stopped

# ============ MEDIA MANAGEMENT ============
seerr:
image: ghcr.io/seerr-team/seerr:latest
Expand Down Expand Up @@ -318,5 +374,6 @@ volumes:
prowlarr:
bazarr:
portainer_data:
prometheus_data:
timescale_data:
redis_data:
66 changes: 0 additions & 66 deletions docs/prometheus.example.yml

This file was deleted.

33 changes: 33 additions & 0 deletions prometheus/prometheus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
global:
scrape_interval: 15s
evaluation_interval: 15s

alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093

scrape_configs:
# Container resource metrics via cAdvisor
- job_name: "cadvisor"
static_configs:
- targets: ["cadvisor:8080"]

# Host metrics (CPU, memory, disk, network) via node_exporter
- job_name: "node_exporter"
static_configs:
- targets: ["node-exporter:9100"]

# Telegraf — uncomment after enabling the prometheus_client output plugin in
# telegraf.conf. The container doesn't expose :9273 by default.
# - job_name: "telegraf"
# static_configs:
# - targets: ["telegraf:9273"]

# Tautulli — uncomment after installing a Prometheus metrics plugin in
# Tautulli. The base container does not expose /metrics natively.
# - job_name: "tautulli"
# metrics_path: "/metrics"
# static_configs:
# - targets: ["tautulli:8181"]
Loading