# Inspector Inspector is a small, opinionated Docker-watchdog. It polls the Docker daemon, applies per-container restart rules, pings Gotify and/or Pushover when things go wrong, and gives you a dashboard that shows what's happening right now and what happened earlier today. Built for home-lab and small-fleet operators who want "it just works" container recovery without standing up a full observability stack. ## What Inspector does - Polls each configured container on a fixed interval and classifies it: `HEALTHY`, `UNHEALTHY`, `STARTING`, `NOT RUNNING`, `ERROR`, `UNREACHABLE`, `CONTAINER NOT FOUND`, `NO HEALTH CHECK`, `UNKNOWN`. - Restarts a container when it crosses a configured error threshold — with cooldowns, grace periods, and an hourly restart cap so a flapping container doesn't DDoS your host. - Opt-in HTTP reachability checks per container (useful for VPN-routed containers whose tunnel drops while Docker still reports "healthy"). - Opt-in Uptime Kuma integration as a second reachability source. - Advanced recovery via Portainer stack redeploy for containers whose network namespace is tied to a VPN sidecar. - Sends notifications on transitions, not on every poll — one line when something breaks, one line when it recovers. - Optional SQLite event history + a daily/weekly digest. - Scheduled maintenance windows (don't act during 2–6 AM). - Manual notification mute per container, from the dashboard. - Embedded web dashboard + events timeline + **web settings page** for editing `config.yml` without SSH. ## What Inspector is not - Not a metrics system. It doesn't scrape Prometheus or push dashboards; use Grafana / Netdata / your real monitoring stack for that. - Not an orchestrator. It only acts on containers you've explicitly listed in `config.yml`. - Not multi-host. One Inspector per Docker socket. ## Install See [`INSTALL.md`](./INSTALL.md). TL;DR: ```bash mkdir -p ~/docker/inspector && cd ~/docker/inspector curl -L -o compose.yml https://gitea.euronvault.com/euronvault/inspector/raw/branch/main/compose.yml curl -L -o config.yml https://gitea.euronvault.com/euronvault/inspector/raw/branch/main/config.example.yml $EDITOR config.yml echo "DOCKER_GID=$(getent group docker | cut -d: -f3)" > .env docker compose up -d ``` ## Configuration Everything lives in `config.yml` next to the compose file. Start from [`config.example.yml`](./config.example.yml) — every key is commented. Highlights: - `inspector.interval_seconds` — poll cadence. - `inspector.dry_run` — classify + log, never restart. - `notifications.gotify` / `notifications.pushover` — enable either, both, or neither. - `containers..max_error_count` / `cooldown_seconds` / `grace_period_seconds` / `max_restarts_per_hour` — the restart knobs. - `containers..restart_on` — which statuses trigger a restart (leave empty for observe-only). - `containers..http_check` — opt-in HTTP ping. - `containers..recovery_action` — `restart_container` (default) or the advanced Portainer-based options. ### Dashboard and settings page If `dashboard.enabled: true`, Inspector exposes a small web UI on `:8080`: - `/` — container cards with current status, error bar, phase (cooldown / grace / failing / observe only / maintenance), and maintenance / mute toggles. - `/events` — per-day timeline of status changes, restarts, recoveries, and failures (requires `database.enabled: true`). - `/settings` — forms for every top-level config section plus one card per container. Saves preserve comments and key order in `config.yml` and keep a `.bak`. Fields that take effect on the next loop iteration (per-container thresholds, `dry_run`, `heartbeat`, `interval_seconds`) hot-apply; fields bound at startup are labeled **requires restart**. Note on `.bak` inside Docker: when `config.yml` is bind-mounted as a single file (the compose default), the `.bak` lives in the container's overlay fs — the live file still updates cleanly, but the backup is lost when the container is removed. To keep the `.bak` on the host, bind-mount the directory instead of the single file. ### Notifications Both channels are independent. If both are enabled, every alert goes to both. Inspector sends on transitions: ``` 🚨 first failure threshold crossed 🔁 container restart triggered ✅ restart / start completed ✅ container recovered 🛑 restart failed / hourly limit exceeded ❓ configured container not found ``` Gotify messages are Markdown; Pushover doesn't render Markdown so the `**bold**` passthrough is cosmetic — bodies are identical. Pushover priorities map from Inspector's Gotify-style scale (default 5) to Pushover's `-2..2` range: `<=2 → -1`, `3..4 → 0`, `5..6 → 1`, `>=7 → 2`. Priority 2 (emergency) ships default `retry` / `expire` and your Pushover client prompts for acknowledgement. ## Docker socket security Inspector talks to `/var/run/docker.sock`. That socket is effectively root on your host: anything that can write to it can start, stop, inspect, and (through bind mounts) read any file as root. Inspector mounts it read-write because it needs to restart containers. Follow the usual home-lab precautions: - Don't publish the dashboard port on an untrusted network. Use a reverse proxy with auth, a tailnet, or SSH tunneling. - Set a strong `dashboard.password`. - Store Portainer / Gotify / Pushover tokens in the config file on a host-only path with permissions `600`. ## Versioning Semantic versioning (`MAJOR.MINOR.PATCH`). Image tags are published for every release; `latest` tracks the newest tag. Pin a specific version in production — see `INSTALL.md`. ## License [MIT](./LICENSE).