| Service | Network Mode | Why |
|---|---|---|
bot |
host |
nmap ARP discovery + BlueZ/D-Bus BLE access |
sniffer |
host |
Must see real LAN ARP/DHCP broadcast traffic |
dashboard |
default bridge | Published to 127.0.0.1:8501 only, reached via Caddy on host network |
caddy |
host |
Single public entry point on port 8109 |
The FastAPI server (uvicorn) inside overwatcher-bot binds 127.0.0.1:8000 only — not 0.0.0.0.
Because bot uses network_mode: host, 127.0.0.1 is the Pi's loopback — it is not directly reachable from the LAN.
External access goes through Caddy: http://<pi-ip>:8109/api/devices (protected by the same basicauth layer as the dashboard) + Bearer token.
Never change the uvicorn bind to
0.0.0.0without also adding firewall rules — doing so would expose the API directly on the LAN with only the Bearer token as protection.
OverwatcherPI requires several container privileges to perform network and hardware monitoring. We operate on a principle of least privilege, mapping only what is absolutely necessary:
network_mode: host: Required by thebotfor nmap/ARP host discovery (needs to inject and read raw frames on the host's subnet) and by thesnifferto see real LAN ARP/DHCP broadcast traffic (bridged networking isolates this traffic).cap_add: NET_RAW, NET_ADMIN: Required by both thebot(for raw packet injection during ARP/ping sweeps via nmap) and thesniffer(for Scapy passive sniffing via raw sockets).apparmor: unconfined& DBus mounts (/var/run/dbus): Required for BLE discovery. Even with the host's/var/run/dbussocket mapped in, Docker's default AppArmor profile aggressively restricts D-Bus communications. Whenbleaksends anAddMatchmethod call to the host's BlueZ daemon, AppArmor blocks it withoutunconfined.vciodevice &vcgencmdaccess: Thebotmaps/dev/vcioand/usr/bin/vcgencmd:roto monitor Raspberry Pi hardware health (CPU throttling, SoC temperature, voltage issues).
Mitigations:
- The
botDockerfile drops root privileges and runs as a dedicated non-root user (overwatcher, uid 1000). - Capabilities like
NET_RAWallow packet sniffing, but the application limits this strictly to internal tooling.
Security Note: Since your
.envcontains sensitive tokens and passwords, it must be locked down on the host. Runchmod 600 .envto prevent unauthorized local read access.
-
Install Docker + Docker Compose plugin:
curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER && newgrp docker
-
Ensure
SNIFFER_INTERFACEis set in.envto your LAN interface (e.g.eth0). -
Regenerate the Caddy password hash for Docker:
docker run --rm caddy:2-alpine caddy hash-password
Update
dashboard/Caddyfilewith the new hash.
# 1. Backup data
cp -r data/ data.bak/ && cp -r logs/ logs.bak/ && cp .env .env.bak
# 2. Stop and disable the old systemd stack
sudo ./overwatcher.sh stop
sudo systemctl disable overwatcher overwatcher-sniffer overwatcher-dashboard overwatcher-caddy
# 3. Build and start containers
docker compose build
docker compose up -d
# 4. Monitor startup
docker compose logs -fAfter startup, confirm:
- Boot notification arrives in Telegram
-
/statusreturns real data -
/networkshows ~same device count as pre-migration -
/bluetoothshows BLE devices (D-Bus passthrough validation) - Dashboard loads at
http://<pi-ip>:8109/with Caddy credentials -
docker compose psshows all 4 services ashealthy
BLE Risk: D-Bus passthrough (
/var/run/dbus:/var/run/dbus) is the highest-risk item in this migration. If BLE scanning stops working, run the bot temporarily outside Docker (python main.pyin a tmux/screen) while keeping the sniffer/dashboard/caddy containerised — a partial migration is better than a broken BLE feature.
docker compose logs -f bot # Follow bot logs
docker compose logs -f sniffer # Follow sniffer logs
docker compose restart bot # Restart single service
docker compose down && docker compose up -d # Full restart
docker compose exec bot python -c "from core.database import DatabaseManager; import asyncio; print(asyncio.run(DatabaseManager.get_active_devices()))"If something goes wrong:
docker compose down
sudo systemctl enable --now overwatcher overwatcher-sniffer overwatcher-dashboard overwatcher-caddyOverwatcherPI creates automated daily DB backups in data/backups/. To restore a backup:
# 1. Stop the bot so it drops the database lock
docker compose stop bot
# 2. Rename the current database (just in case)
mv data/netmon.db data/netmon.db.corrupt
# 3. Copy the chosen backup into place
cp data/backups/netmon-YYYYMMDD.db data/netmon.db
# 4. Start the bot again
docker compose start bot