unified infrastructure visibility
Unified infrastructure visibility platform. Monitor networks, servers, and cloud infrastructure from a single pane of glass. Vendor-agnostic, open source, deployable on-prem via Docker Compose.
spane handles gRPC/gNMI + Cisco MDT streaming telemetry (SNMP polling as fallback), config compliance and backup, CVE intelligence, lifecycle/EOL tracking, log anomaly detection, and unified risk scoring — all open source, all containerized.
Also built: four-tier device discovery + enrichment, LLDP topology mapping, environment telemetry (CPU/memory/temp/fans/PSU), ARP/MAC table collection with OUI lookup, agentless service checks (HTTP/HTTPS/TCP/ICMP/DNS/TLS/SMTP/SSH), reachability/ping-latency monitoring, alerting with team routing + escalation (email/Slack/Discord) and maintenance windows, and SSO (Google OAuth2, Stage 1).
- Device inventory with weighted compliance scores (A–F)
- SNMP polling + AOS-CX REST API enrichment
- Interface utilization & statistics
- Environment monitoring (temp, fans, PSU) + PoE budget tracking with alerts
- Reachability/ping-latency monitoring
- WAN circuit tracking — provider/contract/cost, ISP IP assignment, bandwidth
- Interface utilization vs bandwidth %, 95th-percentile trending, contract-expiry alerts
- Manual topology links for LLDP-less devices + automatic LLDP/CDP discovery
- Jinja2 template engine, LLDP-aware interface rules, role-consistency checks
- Running-vs-startup config check, weighted scoring (A–F), daily automated runs
- Full config backup (AOS-CX SSH + REST), change detection & diffs
- Running/startup mismatch alerts, collection-health tracking
- NetFlow v5/v9/IPFIX (UDP 2055) + sFlow (UDP 6343)
- DNS hostname enrichment toggle, flow-threshold alerts
- Juniper Mist + UniFi integrations with AP/console telemetry; TV dashboards
- Daily/weekly/monthly/quarterly + compliance reports
- TV/NOC dashboards (
/tv) with auto-rotation - Bulk alert actions, audit logging, RBAC, OpenBao-backed secrets
- Docker-Compose deploy, AES-256 backups (local/SCP/Git/S3), watchdog auto-recovery
- Safe update (
./netpulse.sh update) with DB backup + rollback - spane Agent (Linux/Windows/ARM64) over OpenBao-PKI mTLS
curl -fsSL https://raw.githubusercontent.com/travisjohnsonga/netpulse/main/scripts/install.sh | bashRequires: Ubuntu 22.04 or 24.04 LTS. Installs: Docker, Docker Compose, and spane, then runs the interactive setup.
If the installer appears to hang, it is almost always waiting for your
sudopassword (the curl pipe can't forward the prompt cleanly). Cache your credentials first, then pipe to bash:sudo -v # cache sudo credentials up front curl -fsSL https://raw.githubusercontent.com/travisjohnsonga/netpulse/main/scripts/install.sh | bashOr download and run it directly so prompts work normally:
curl -fsSL https://raw.githubusercontent.com/travisjohnsonga/netpulse/main/scripts/install.sh -o install.sh chmod +x install.sh ./install.sh # runs interactivelyWhen piped, the installer runs non-interactively: apt prompts are pre-answered and setup falls back to safe defaults (auto-generated secrets, dev ports). Re-run
./scripts/setup.shfrom a terminal to customise.
NETPULSE_DIR=/opt/netpulse curl -fsSL https://raw.githubusercontent.com/travisjohnsonga/netpulse/main/scripts/install.sh | bash
# or
curl -fsSL https://raw.githubusercontent.com/travisjohnsonga/netpulse/main/scripts/install.sh | bash -s -- --dir /opt/netpulseSecurity note: Always review scripts before running them with
curl | bash. Read this one first: https://github.com/travisjohnsonga/netpulse/blob/main/scripts/install.sh
Size the host to your fleet.
- Ubuntu 22.04 LTS
- Ubuntu 24.04 LTS (recommended)
Other Linux distributions may work but are not tested or supported.
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 4 cores | 4 cores |
| RAM | 8 GB | 16 GB |
| Disk | 50 GB | 100 GB SSD |
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 8 cores | 16 cores |
| RAM | 16 GB | 32 GB |
| Disk | 100 GB | 500 GB SSD |
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 16 cores | 32 cores |
| RAM | 32 GB | 64 GB |
| Disk | 500 GB | 2 TB SSD |
| Note | Kubernetes deployment (Helm chart planned) |
- SSD strongly recommended for InfluxDB + OpenSearch
- Disk usage grows with number of devices, log volume, config retention, NetFlow/sFlow
- RAM dominated by OpenSearch (4 GB min) and InfluxDB (2 GB min)
- CPU spikes during discovery scans (nmap)
- gNMI streaming: add ~50 MB RAM per 100 devices
-
Docker Engine (v24.0+)
curl -fsSL https://get.docker.com | sh sudo usermod -aG docker $USER newgrp docker
-
Docker Compose (v2.20+) — included with Docker Desktop.
For Linux:
sudo apt-get install docker-compose-plugin
Verify:
docker compose version # Docker Compose version v2.x.x -
Git
sudo apt-get install git
- nmap (for network discovery on the host):
sudo apt-get install nmap. Also installed inside the containers automatically. - Python 3.11+ — for helper scripts only; not required to run spane.
Ports that must be accessible on the spane server:
| Port | Protocol | Service | Direction |
|---|---|---|---|
| 443 | TCP | HTTPS Web UI | Inbound |
| 80 | TCP | HTTP (redirects to HTTPS) | Inbound |
| 514 | UDP | Syslog receiver | Inbound from devices |
| 57400 | TCP | gNMI/MDT streaming | Inbound from devices |
| 2055 | UDP | NetFlow/sFlow | Inbound from devices |
| 161 | UDP | SNMP (outbound polls) | Outbound to devices |
| 22 | TCP | SSH (config collection) | Outbound to devices |
spane automatically configures iptables to NAT container traffic through the host IP. This ensures network devices see the host server IP for SNMP and SSH connections.
Requirements:
sudoaccess on the host serveriptablesavailable (standard on Linux)iptables-persistentrecommended for persistence across reboots (used bysetup.shwhen available)
Why this is needed:
Network devices typically restrict SNMP and SSH access to specific management
IPs. Without NAT, Docker containers would connect from the Docker bridge subnet
(e.g. 172.18.x.x), which is usually not in device ACLs. The MASQUERADE rule
makes all container traffic appear to come from the host IP instead.
If SNMP stops working after a reboot:
sudo ./netpulse.sh fix-natFor full telemetry support, network devices need:
- SNMP v3 configured (recommended) or v2c
- SSH access for config collection
- Syslog forwarding to the spane IP
- gNMI/MDT (optional, for streaming telemetry)
- Cisco IOS-XE: MDT on port 57400
- See Settings → Telemetry Configuration for device-specific configuration snippets
git clone https://github.com/travisjohnsonga/netpulse.git
cd netpulse./scripts/setup.shThis generates secure random secrets, initialises credential storage (OpenBao), creates the admin account, and configures all services.
Note: the admin password must be 12+ characters.
docker compose up -dFirst startup takes 2-3 minutes while services initialise. Monitor progress:
docker compose logs -f apiWait for: starting: gunicorn config.wsgi:application.
./scripts/download_mibs.shDownloads ~1,800 vendor MIBs for SNMP OID resolution — Cisco (1650),
Fortinet (39), AOS-CX (35), Aruba (25), Juniper (13), Arista (8), Palo Alto (7),
SonicWall (4), Standard RFC (10). setup.sh runs this for you; re-run after
updates. Files are git-ignored (see the "SNMP MIBs" section below).
https://YOUR_SERVER_IP
Log in with the admin credentials set during setup.
Note: the default install uses a self-signed certificate, so your browser shows a security warning — this is expected. Add a proper TLS certificate in Settings → SSL for production use.
Default (standard ports):
http://YOUR_IP → redirects to HTTPS
https://YOUR_IP → spane UI
Binding 80/443 needs root or CAP_NET_BIND_SERVICE — Docker handles this when
it publishes the ports, so no extra config is needed. Add a proper TLS cert
(Settings → SSL/TLS) for internet-facing use.
Development (non-privileged ports) — set in .env (setup.sh can do this for
you):
FRONTEND_PORT=3000
FRONTEND_HTTPS_PORT=3443
then docker compose down && docker compose up -d. Access
http://YOUR_IP:3000 (redirects) / https://YOUR_IP:3443. The nginx container
always listens on 80/443 internally; only the host port mapping changes.
- Go to Settings → Discovery
- Create a new job with your network subnet
- Approve discovered devices
- Or manually: Devices → + Add Device
OpenBao runs with persistent file storage and is initialised + unsealed
automatically on first start — no manual operator init/unseal needed. The
api service writes the unseal key + root token to /openbao/data/.init_keys
(mode 600) on the openbao-data volume and auto-unseals from it on later
starts, so device credentials, git tokens and feed API keys persist across
restarts.
⚠️ /openbao/data/.init_keysholds your unseal key and root token. It lives on the Docker volume and is git-ignored — never commit it, and back it up securely (losing it means you cannot unseal OpenBao after a restart). LeaveOPENBAO_TOKENblank in.envunless using an externally-managed token.
To start spane automatically on reboot:
./netpulse.sh install-serviceThis installs and enables two systemd units — netpulse.service (runs
docker compose up -d on boot) and netpulse-nat.service (re-applies the Docker
NAT rule once the stack is up). setup.sh also offers to do this during initial
setup.
./netpulse.sh service-status # show service status
./netpulse.sh uninstall-service # disable + remove the service
sudo systemctl start netpulse # start now
sudo journalctl -u netpulse -f # follow boot logsManual install (equivalent)
sudo tee /etc/systemd/system/netpulse.service << 'EOF'
[Unit]
Description=spane Network Intelligence Platform
Requires=docker.service
After=docker.service network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
User=YOUR_USERNAME
WorkingDirectory=/path/to/netpulse
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=300
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable netpulse.serviceAlways use the update command rather than running docker compose by hand — it
applies migrations and back-fills new config that a plain up -d would miss:
./netpulse.sh update # add --yes to skip the confirmation promptThe update flow:
- Tags a rollback point (
pre-update-<timestamp>git tag). - Shows current vs latest version and what changed, then asks to confirm.
- Pulls
origin/main(fast-forward only; refuses a dirty tree). - Back-fills new
.envvariables from.env.example(warns which it added). - Backs up the database (
pg_dump) to.update-db-backup-*.sql.gz. - Rebuilds the changed services (version stamped into the image).
- Applies migrations explicitly, then re-applies the Docker NAT rule.
- Verifies
/api/health/— on failure it prints the DB-backup path and the rollback command, and exits non-zero.
Each run appends a line to .update-history.log.
./netpulse.sh show-version # running version + recent update history
./netpulse.sh rollback # pick a pre-update snapshot tag and rebuildRollback reverts code (and rebuilds), not the database. If a migration must be undone, restore the matching
.update-db-backup-*.sql.gzfirst.
The running version is also shown in the sidebar (a v1.0.NNN badge that turns
amber with ↑ when an update is available — GET /api/version/check/ compares
against GitHub) and returned by GET /api/health/.
Update checks hit the public GitHub repo (no token needed). For a private repo
set GITHUB_TOKEN in .env, or disable with VERSION_CHECK_ENABLED=false.
⚠️ DESTROYS ALL DATA — development use only.
./scripts/factory-reset.shAfter reset, re-run setup:
./scripts/setup.sh
./netpulse.sh rebuild-api24 Docker services including:
- Django 6.0 REST API
- React + TypeScript frontend
- InfluxDB (time-series metrics)
- PostgreSQL (primary database)
- OpenSearch (logs and flows)
- OpenBao (secrets management)
- NATS JetStream (message bus)
- Valkey (cache + WebSocket broker)
See docs/ARCHITECTURE.md for full details.
- Architecture
- Metrics & Alerting Reference
- Custom Alerts & InfluxDB Metrics
- Config Push Templates
- Reports
- Backup & Restore
- TV Dashboards
- Agent Installation
| Platform | SNMP | SSH | gNMI | Syslog |
|---|---|---|---|---|
| Cisco IOS-XE | ✅ | ✅ | ✅ | ✅ |
| Cisco IOS | ✅ | ✅ | — | ✅ |
| Cisco IOS-XR | ✅ | ✅ | ✅ | ✅ |
| Cisco NX-OS | ✅ | ✅ | ✅ | ✅ |
| Juniper JunOS | ✅ | ✅ | ✅ | ✅ |
| Arista EOS | ✅ | ✅ | ✅ | ✅ |
| Fortinet FortiOS | ✅ | ✅ | — | ✅ |
| Palo Alto PAN-OS | ✅ | ✅ | — | ✅ |
| SonicWall (v7) | ✅ | ✅ | — | ✅ |
| SonicWall (v8) | ✅ | ✅ | — | ✅ |
| HPE AOS-CX | ✅ | ✅ | 📋 | ✅ |
| Aruba AOS | ✅ | ✅ | — | ✅ |
| Platform | SNMP | SSH | Config Backup | ARP/MAC | Environment |
|---|---|---|---|---|---|
| Cisco IOS/IOS-XE | ✅ | ✅ | ✅ | ✅ | ❌ |
| HPE AOS-CX | ✅ | ✅ | ✅ | ✅ | ✅ |
| Fortinet FortiOS | ✅ | ✅ | ✅ | ✅ | ❌ |
| SonicWall (v7) | ✅ | ✅ | ✅ | ❌ | |
| SonicWall (v8) | ✅ | ✅ | ✅ | ✅ | ❌ |
Notes:
- SonicWall v7 config backup via REST API requires the built-in
adminaccount — user accounts returnAPI_AUTH_USER_CAN_MGMTand get 401 on/config/current. v8 works with any admin account. See docs/platforms/sonicwall.md. - SonicWall environment (temp/fans/PSU) is not exposed via SNMP or REST on any version — the Environment tab shows "No environment data"; CPU/Memory are on the Telemetry tab.
- AOS-CX environment telemetry (CPU/memory/temp/fans/PSU) is SNMP-based and
validated on a real HPE 6100. Aruba Central keepalive logs (
hpe-restd) are normal. See docs/platforms/aos_cx.md.
Per-platform guides live in docs/platforms/.
spane resolves SNMP OIDs to human-readable names using MIB files under
mibs/. Download the publicly available vendor + standard MIBs with:
./scripts/download_mibs.shThis fetches MIBs from public sources:
- Standard RFC MIBs — github.com/net-snmp/net-snmp
- Cisco — github.com/cisco/cisco-mibs
- All other vendors — github.com/librenms/librenms (
mibs/, organised by vendor): Fortinet, Juniper, Arista, Aruba AOS, Aruba AOS-CX, SonicWall, Palo Alto, MikroTik, and 100+ more.
Run it after first install and after updates (~1800 MIB files). Downloaded
collections are git-ignored (too large to commit) — only the per-vendor
README.md files are tracked. Upload site-specific MIBs in the UI
(Settings → MIB Files) → saved to mibs/custom/. The tree is mounted into
the api and ingest-snmp containers for OID resolution.
Network devices that restrict management by source IP must see the host IP, not the Docker bridge subnet. Re-apply the NAT (MASQUERADE) rule:
sudo ./netpulse.sh fix-natThen confirm the device allows SNMP from the host IP. See the Container NAT section above and docs/setup/nat.md.
SonicOS v7 requires the built-in admin account for REST API config backup.
User accounts return API_AUTH_USER_CAN_MGMT and get 401 on
/config/current. Workaround: put the built-in admin credentials in the device's
HTTPS credential profile, or use the SSH CLI backup. (v8 works with any admin
account.) Full details: docs/platforms/sonicwall.md.
After a device firmware update the host key changes. Clear the cached key:
docker compose exec api ssh-keygen -R {device_ip}Check the credential profile status:
./netpulse.sh credentials --show-secretsIf it shows placeholder values, OpenBao did not retain the secret (e.g. the volume was wiped) — re-enter the credentials via Settings → Credentials.
Apache 2.0 — see LICENSE for details.
Community contributions welcome!
- SNMP MIB files
- TextFSM templates
- Vendor advisory YAML files
- Platform-specific telemetry paths
- Bug reports and feature requests