Network device scanner for Proxmox LXC (Debian 13 / Trixie).
Scans the local network every 5 minutes for new devices (arp-scan),
runs detailed scans (nmap -O -sV) every 6 hours, stores everything in SQLite
and provides a web UI for viewing, searching, filtering, exporting and configuration.
Newly discovered devices trigger a Gotify notification.
Language: English (German version: README.de.md) — UI supports DE/EN with browser auto-detection and cookie persistence (switch via navbar).
- Debian 13 (Trixie) – Python 3.12+
arp-scan,nmap,sudo(container needsCAP_NET_RAW)python3-venv
LXC must allow
CAP_NET_RAW, otherwisearp-scancannot send Layer-2 packets. In the Proxmox container config (/etc/pve/lxc/CTID.conf) ensure nolxc.cap.dropcontainsnet_raw. Unprivileged containers additionally need/dev/net/just_network_access– usuallyCAP_NET_RAWup to25is sufficient.
The installer scripts/install.sh performs all steps automatically:
# as root in LXC
bash scripts/install.shThe installer asks interactively for:
- System user (default:
netwatcher) - Directories (default:
/opt/netwatcher) - Web UI host/port
- Admin username and password
Afterwards all systemd units are active and the web UI is reachable.
Non-interactive installation (CI/automation):
INTERACTIVE=0 bash scripts/install.shAfter a code update, only update units + web service:
bash scripts/install.sh --updateUninstall:
bash scripts/install.sh --uninstall# as root in LXC
apt update
apt install -y python3-venv python3-pip arp-scan nmap sudo
useradd --system --create-home --home-dir /opt/netwatcher netwatcher
sudo -u netwatcher git clone <repo-url> /opt/netwatcher/app
sudo -u netwatcher bash -lc '
cd /opt/netwatcher/app
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
'
export NETWATCHER_DB=/opt/netwatcher/data/netwatcher.db
export NETWATCHER_SECRET="$(openssl rand -hex 32)"
mkdir -p /opt/netwatcher/data
chown -R netwatcher:netwatcher /opt/netwatcher/data
sudo -u netwatcher bash -lc "
export NETWATCHER_DB=$NETWATCHER_DB
cd /opt/netwatcher/app
.venv/bin/python -m netwatcher init-db
.venv/bin/python -m netwatcher add-user admin
"Templates in scripts/ contain placeholders (__NETWATCHER_USER__ etc.).
The install.sh installer fills them automatically. For manual installation:
| Placeholder | Example |
|---|---|
__NETWATCHER_USER__ |
netwatcher |
__NETWATCHER_DIR__ |
/opt/netwatcher/app |
__NETWATCHER_DB__ |
/opt/netwatcher/data/netwatcher.db |
__NETWATCHER_DB_DIR__ |
/opt/netwatcher/data |
__NETWATCHER_BIN__ |
/opt/netwatcher/app/.venv/bin/python -m netwatcher |
__GUNICORN_BIN__ |
/opt/netwatcher/app/.venv/bin/gunicorn |
__NETWATCHER_SECRET__ |
<Secret from Setup step> |
__WEB_BIND__ |
0.0.0.0 |
__WEB_PORT__ |
5000 |
# Replace placeholders via sed (example: SERVICEFILE)
SUD_BIN="/opt/netwatcher/app/.venv/bin/python -m netwatcher"
for f in scripts/netwatcher-*.service; do
sed -e "s|__NETWATCHER_USER__|netwatcher|g" \
-e "s|__NETWATCHER_DIR__|/opt/netwatcher/app|g" \
-e "s|__NETWATCHER_DB__|/opt/netwatcher/data/netwatcher.db|g" \
-e "s|__NETWATCHER_DB_DIR__|/opt/netwatcher/data|g" \
-e "s|__NETWATCHER_BIN__|$SUD_BIN|g" \
-e "s|__GUNICORN_BIN__|/opt/netwatcher/app/.venv/bin/gunicorn|g" \
-e "s|__NETWATCHER_SECRET__|$NETWATCHER_SECRET|g" \
-e "s|__WEB_BIND__|0.0.0.0|g" \
-e "s|__WEB_PORT__|5000|g" \
$f > /etc/systemd/system/$(basename $f)
done
cp scripts/netwatcher-*.timer /etc/systemd/system/
systemctl daemon-reload
mkdir -p /opt/netwatcher/data
chown -R netwatcher:netwatcher /opt/netwatcher/data
systemctl enable --now netwatcher-web.service
systemctl enable --now netwatcher-scan.timer
systemctl enable --now netwatcher-detail.timer- Web UI:
http://<lxc-ip>:5000/ - Login with the created user
- Language: auto-detected from browser (
Accept-Language), switch via navbar DE/EN, stored in cookie andlanguageconfig (also used for Gotify/MQTT notifications) - Devices: search/filter/sort, show/hide columns, inline editing (name/status/notes/tags)
- Export: CSV/JSON via buttons in device list
- Configuration: IP range, interface, scan interval, detail scan every 6h, DNS/mDNS/IPv6/HTTP/TLS/UPnP/SMB, Gotify, OPNsense sync, date format (auto from language)
- External integrations: arpwatch (
/var/lib/arpwatch/arp.dat), LibreNMS REST API and Greenbone report URL; all optional and syncable via configuration - LibreNMS and Greenbone are hidden by default. Enable server-side with
NETWATCHER_REMOTE_INTEGRATIONS=1during install or systemd update. - "Scan now" / "Detail scan" triggers manual scans
- Scan profiles: Quick, Detail and Full (all ports) via device list
- OPNsense Dnsmasq integration: read static DHCP hosts and active leases via API, merge IPv4/IPv6 and match by MAC
- Manufacturers page with device counts, search, sorting and direct manufacturer filter in device list
python -m netwatcher init-db # initialize DB
python -m netwatcher add-user <name> # create web UI user
python -m netwatcher scan # run arp scan now (like timer)
python -m netwatcher detail-scan # nmap detail for all devices
python -m netwatcher cleanup-history # delete yesterday's history
python -m netwatcher opnsense-sync # sync OPNsense Dnsmasq hosts
python -m netwatcher profile-scan --profile quick # top 20 ports
python -m netwatcher profile-scan --profile detail # OS and services
python -m netwatcher profile-scan --profile full # all TCP ports
python -m netwatcher integrations-sync # arpwatch, LibreNMS, Greenbone
python -m netwatcher mqtt-sync # publish all devices to MQTT
python -m netwatcher serve # Flask dev server (test only).venv/bin/pip install pytest
.venv/bin/python -m pytest tests/ -vTests run fully offline (arp-scan fixtures instead of real scans, Gotify endpoint is mocked).
See netwatcher/db.py. Tables:
devices: all ever found devices (unique by MAC)scan_history: each scan per device + type (arp/detail/offline)config: key/value config (editable in web UI, includeslanguage)users: web UI logins (bcrypt hashes)tags/device_tags: device grouping (many-to-many)
In the web UI under Configuration:
Gotify Server URL(e.g.https://gotify.example.com)App Token(Gotify app token, not client token!)- "Send test message" checks the connection
For new devices, offline devices or new ports a message with IP/MAC/manufacturer is sent (language per language config).