NetProbe

NetProbe

Docker app from bmartino1's Repository

Overview

Netprobe 2.0 – Internet Quality Dashboard

Netprobe 2.0 is a lightweight, container-friendly network probe and web UI for home/lab internet monitoring.

It periodically:

Pings your gateway, router, and a list of “anchor” sites.
Measures packet loss, latency, and jitter.
Measures DNS lookup latency per DNS server.
Runs scheduled and manual Speedtest.net tests.
Stores everything in SQLite and renders a dark-mode dashboard with Chart.js.
The goal is a simple “drop in and forget it” quality monitor that you can run on Unraid, Proxmox, Docker, etc.

Additional variables can be found here: https://github.com/bmartino1/NetProbe_Python

Optional data:
ALL are OPTIONAL! Defaults/fallbacks to SQLite.

Optional: if ping needs extra caps in your environment

--cap-add=NET_RAW --cap-add=NET_ADMIN --cap-add=SYS_ADMIN

Use standalone Postgres for backend storage docker variables...

(May be hidden in template)
USE_POSTGRES: ${USE_POSTGRES:-true}
DB_ENGINE: ${DB_ENGINE:-postgres}

Postgres connection details (match the Postgres service above via env file...)

POSTGRES_HOST: ${POSTGRES_HOST:-postgres}
POSTGRES_PORT: ${POSTGRES_PORT:-5432}
POSTGRES_DB: ${POSTGRES_DB:-netprobe}
POSTGRES_USER: ${POSTGRES_USER:-netprobe}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-netprobe}

Netprobe 2.0 – Internet Quality Dashboard

Netprobe 2.0 is a lightweight, container-friendly network probe and web UI for home / lab internet monitoring.

It periodically:

  • Pings your gateway, router and a list of “anchor” sites.
  • Measures packet loss, latency and jitter.
  • Measures DNS lookup latency per DNS server.
  • Runs scheduled and manual Speedtest.net tests.
  • Stores everything in SQLite and renders a dark-mode dashboard with Chart.js.

The goal is a simple “drop in and forget it” quality monitor that you can run on Unraid, Proxmox, Docker, etc.


Features

  • Internet Quality Score (0–100)
    Weighted composite of loss, latency, jitter and DNS response time.

  • Per-metric panels

    • Packet Loss
    • Latency to anchors
    • Jitter
    • DNS Response Time (with lines + checkboxes for each configured DNS server)
    • Bandwidth (download / upload history from speedtest)
  • History controls
    Time selector on each chart (seconds → months).

  • Panel toggles
    Checkboxes to show/hide sections (Internet Quality, Loss, Latency, Jitter, DNS, Bandwidth). Preference is saved in localStorage per browser.

  • Speedtest integration

    • Automatic periodic runs (SPEEDTEST_INTERVAL).
    • “Run Speedtest Now” button in UI.
    • Last result summary in the top status bar.
    • Download / upload charts.
  • SQLite storage

    • measurements – aggregate probe results.
    • dns_measurements – per-DNS-server latency per probe.
    • speedtests – speedtest history.
  • Single-container deployment

    • Flask + Gunicorn backend
    • Chart.js frontend
    • Works nicely with Docker Compose and Unraid templates.

How it works

Every PROBE_INTERVAL seconds the probe loop:

  1. Detects the container’s default gateway.
  2. Pings:
    • Default gateway (inside Docker network)
    • Optional ROUTER_IP (your LAN router)
    • Each hostname in SITES
  3. Computes average latency, jitter (max–min) and packet loss across all ping targets.
  4. For each configured DNS server (DNS_NAMESERVER_X_IP), it measures the time to resolve DNS_TEST_SITE several times and averages the result.
  5. Computes an Internet Quality Score using weighted, threshold-normalized metrics.
  6. Stores:
    • Aggregate metrics in measurements
    • Per-server DNS results in dns_measurements

Separately, a periodic task runs speedtest when at least SPEEDTEST_INTERVAL seconds have passed since the last run and stores the result in speedtests.

The frontend polls the APIs (JSON) and renders gauges + history charts.


Quick start (Docker Run)

docker run -d \
  --name netprobe \
  --restart unless-stopped \
  -p 8080:8080 \
  -v /mnt/user/appdata/netprobe/database:/data \
  -e DB_ENGINE=sqlite \
  -e USE_POSTGRES=false \
  -e ROUTER_IP=192.168.1.1 \
  -e DNS_NAMESERVER_4="LAN_DNS" \
  -e DNS_NAMESERVER_4_IP=192.168.1.1 \
  bmmbmm01/netprobe

Quick start (Docker Compose)

# docker-compose.yml
services:
  netprobe:
    image: bmmbmm01/netprobe:latest
    #build: ./probe #github clone and build run your own...
    container_name: netprobe
    restart: unless-stopped

    # Optional: use a separate env file instead of inline vars
    # env_file:
    #   - ./probe/config.env

    environment:
      WEB_PORT: 8080
      DB_PATH: /data/netprobe.sqlite
      PROBE_INTERVAL: 60
      PING_COUNT: 4

      SITES: fast.com,google.com,youtube.com
      ROUTER_IP: 192.168.1.1

      DNS_TEST_SITE: google.com
      DNS_NAMESERVER_1: Google_DNS
      DNS_NAMESERVER_1_IP: 8.8.8.8
      DNS_NAMESERVER_2: Quad9_DNS
      DNS_NAMESERVER_2_IP: 9.9.9.9
      DNS_NAMESERVER_3: CloudFlare_DNS
      DNS_NAMESERVER_3_IP: 1.1.1.1
      DNS_NAMESERVER_4: My_DNS_Server
      DNS_NAMESERVER_4_IP: 192.168.1.1

      WEIGHT_LOSS: 0.6
      WEIGHT_LATENCY: 0.15
      WEIGHT_JITTER: 0.2
      WEIGHT_DNS_LATENCY: 0.05

      THRESHOLD_LOSS: 5
      THRESHOLD_LATENCY: 100
      THRESHOLD_JITTER: 30
      THRESHOLD_DNS_LATENCY: 100

      SPEEDTEST_ENABLED: "True"
      SPEEDTEST_INTERVAL: 14400   # 4 hours
      APP_TIMEZONE: UTC

    ports:
      - "8080:8080"

    volumes:
      - netprobe_data:/data

    cap_add:
      - NET_RAW
      - NET_ADMIN
      - SYS_ADMIN

volumes:
  netprobe_data:

** Both Quick Starts use the sqlfile option for longterm persitent data... Postgres backend changeover is available via additional docker options later


Environment variables

Variable Default Description
WEB_PORT 8080 Port inside container for the web UI / API.
DB_PATH /data/netprobe.sqlite SQLite DB path (used when DB_ENGINE=sqlite).
DB_ENGINE sqlite Database backend: sqlite or postgres.
USE_POSTGRES (empty) Legacy flag; if true, forces Postgres unless DB_ENGINE is set.
POSTGRES_HOST postgres Postgres host when DB_ENGINE=postgres or USE_POSTGRES=true.
POSTGRES_PORT 5432 Postgres TCP port.
POSTGRES_DB netprobe Postgres database name.
POSTGRES_USER netprobe Postgres username.
POSTGRES_PASSWORD netprobe Postgres password.
PROBE_INTERVAL 30 Seconds between probe runs.
PING_COUNT 20 ICMP packets per target per probe.
APP_TIMEZONE UTC Label shown in UI (no TZ conversion yet).
SITES fast.com,google.com,youtube.com,amazon.com Comma-separated ping targets.
ROUTER_IP (empty) Optional LAN router IP.
DNS_TEST_SITE google.com Domain for DNS latency tests.
DNS_NAMESERVER_1..4 (labels) Human-readable DNS names for UI.
DNS_NAMESERVER_1..4_IP (IPs) DNS IPs to probe.
WEIGHT_LOSS 0.6 Weight of packet loss in score (0–1, sum = 1).
WEIGHT_LATENCY 0.15 Weight of latency.
WEIGHT_JITTER 0.2 Weight of jitter.
WEIGHT_DNS_LATENCY 0.05 Weight of DNS latency.
THRESHOLD_LOSS 5 Loss % considered “max bad” for scoring.
THRESHOLD_LATENCY 100 Latency ms considered “max bad”.
THRESHOLD_JITTER 30 Jitter ms considered “max bad”.
THRESHOLD_DNS_LATENCY 100 DNS ms considered “max bad”.
SPEEDTEST_ENABLED True Enable periodic speedtests.
SPEEDTEST_INTERVAL 14400 Seconds between automatic speedtests.
SPEEDTEST_SERVER "" Optional Speedtest server ID. Leave blank to use automatic server selection.
LIVE_LOG_POLL_SECONDS 2 Seconds between live log viewer refreshes in the web UI.

You can also put these in config.env and uncomment env_file in the Compose file.


API code overview...

The frontend uses these JSON endpoints (you can also query them yourself by calling the python venv...):

  • GET / – main UI.

  • GET /api/score/recent?limit=N
    Recent aggregate data. Each row includes:

    • ts, iso
    • avg_latency_ms, avg_jitter_ms, avg_loss_pct
    • avg_dns_latency_ms
    • score (0–100)
    • dns_per_server – optional { "<dns_ip>": latency_ms } map.
  • GET /api/score/latest
    Most recent probe (same fields as above).

  • GET /api/config
    Effective configuration (after env overrides), including:

    • probe interval, ping count, timezone label
    • resolved gateway IP
    • router IP
    • sites
    • DNS test site
    • dns_servers_detail – list of { name, ip }
    • weights / thresholds
    • speedtest settings.
  • GET /api/speedtest/history?limit=N
    Speedtest history, newest → oldest, each with:

    • ts, iso
    • ping_ms
    • download_mbps, upload_mbps
    • server_name, server_host, server_country.
  • GET /api/speedtest/latest
    Most recent speedtest result.

  • POST /api/speedtest/run
    Trigger an immediate speedtest. Returns:

    {
      "success": true,
      "result": {
        "timestamp": 1234567890,
        "ping_ms": 6.1,
        "download_mbps": 100.0,
        "upload_mbps": 20.0,
        "server": {
          "name": "Example ISP",
          "host": "speed.example.com",
          "country": "US"
        }
      }
    }
    

Find a Speedtest Server ID

If you want to manually target a specific Speedtest server, you can list available server IDs from inside the running Netprobe container.

Command

docker exec -it netprobe speedtest-cli --list

This returns a list of nearby Speedtest servers with their numeric IDs.

Example output

12345) Example ISP - Chicago, IL (12.34 km)
23456) Another ISP - Rockford, IL (88.12 km)
34567) Test Provider - Milwaukee, WI (140.55 km)

In this example, the server IDs are:

  • 12345
  • 23456
  • 34567

You can then use one of those IDs with Netprobe.

Troubleshooting

Charts all show 100% loss / very high latency

  • Let it run for 5 min... internet average takes time to build per defualt weights...

  • Ensure the container has the needed capabilities:

    • NET_RAW, NET_ADMIN, SYS_ADMIN
  • From the host, verify basic connectivity from inside the container:

    docker exec -it netprobe ping -c 3 8.8.8.8
    
  • If this fails, fix host networking / firewall before debugging Netprobe.


DNS panel is flat or empty

  • Confirm DNS_TEST_SITE resolves inside the container:

    docker exec -it netprobe nslookup google.com 8.8.8.8
    
  • Verify that DNS_NAMESERVER_X_IP values are correct and reachable.

  • Click Show Config / Env in the UI to confirm DNS servers are parsed as expected.

Speedtest never runs

  • Check that:
echo $SPEEDTEST_ENABLED
# should be: True
  • Look at logs:
docker logs netprobe
  • You should see lines like:

    Speedtest: ping=… down=… up=…
    
  • Remember automatic runs only happen every SPEEDTEST_INTERVAL seconds. (by default every 4 hours) you can manuly run or set this interval in the docker env...)

  • Use the Run Speedtest Now button in the UI to verify it works on demand

Install NetProbe on Unraid in a few clicks.

Find NetProbe in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for NetProbe Review the template variables and paths Click Install

Requirements

ALL are OPTIONAL! Defaults/fallbacks to SQLite

# Optional: if ping needs extra caps in your environment
--cap-add=NET_RAW --cap-add=NET_ADMIN --cap-add=SYS_ADMIN


# Use standalone Postgres for backend storage docker variables...

USE_POSTGRES: ${USE_POSTGRES:-true}
DB_ENGINE: ${DB_ENGINE:-postgres}
    
# Postgres connection details (match the Postgres service above via env file...)

POSTGRES_HOST: ${POSTGRES_HOST:-postgres}
POSTGRES_PORT: ${POSTGRES_PORT:-5432}
POSTGRES_DB: ${POSTGRES_DB:-netprobe}
POSTGRES_USER: ${POSTGRES_USER:-netprobe}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-netprobe}

Download Statistics

5,395
Total Downloads

Related apps

Details

Repository
bmmbmm01/netprobe:latest
Last Updated2026-04-08
First Seen2025-12-01

Runtime arguments

Web UI
http://[IP]:[PORT:8080]
Network
bridge
Shell
bash
Privileged
false

Template configuration

DockerNatPortPorttcp
Target
8080
Default
8080
Value
8080
Database pathPathrw

Container /data location for SQLite file that holds long-term data. Container path can be changed if DB_PATH variable is assigned. DB_PATH defaults to /data/netprobe.sqlite

Target
/data/
Default
/mnt/user/appdata/netprobe_database
Value
/mnt/user/appdata/netprobe_database
PROBE_INTERVALVariable

Seconds between script probe runs. (Countdown in web page.) This runs the pings, DNS lookups, and others to collect data...

Default
30
Value
30
PING_COUNTVariable

ICMP packets per target per probe.

Default
20
Value
4
APP_TIMEZONEVariable

Time zone, e.g. America/Chicago

Default
UTC
Value
UTC
SITESVariable

Comma-separated ping targets.

Default
fast.com,google.com,youtube.com
Value
fast.com,google.com,youtube.com
ROUTER_IPVariable

Optional LAN router IP. Your router/gateway IP address to help with pings. Can leave blank (to leave blank, delete this variable).

Default
192.168.1.1
Value
192.168.1.1
DNS_TEST_SITEVariable

Domain for DNS latency tests.

Default
google.com
Value
google.com
DNS_NAMESERVER_4Variable

Web UI display name

Default
My_DNS_Server
Value
My_DNS_Server
DNS_NAMESERVER_4_IPVariable

DNS IPs to probe. Do you run a custom DNS server? Set this to that IP.

Default
192.168.1.1
Value
192.168.1.1
SPEEDTEST_ENABLEDVariable

Enable periodic speed tests at SPEEDTEST_INTERVAL in seconds.

Default
True
Value
True
SPEEDTEST_INTERVALVariable

14400 seconds = 4 hours. Seconds between automatic speed tests.

Default
14400
Value
14400
DNS_NAMESERVER_1Variable

Google_DNS

Default
Google_DNS
Value
Google_DNS
DNS_NAMESERVER_1_IPVariable

Google_DNS 8.8.8.8

Default
8.8.8.8
Value
8.8.8.8
DNS_NAMESERVER_2Variable

Quad9_DNS

Default
Quad9_DNS
Value
Quad9_DNS
DNS_NAMESERVER_2_IPVariable

Quad9_DNS 9.9.9.9

Default
9.9.9.9
Value
9.9.9.9
DNS_NAMESERVER_3Variable

CloudFlare_DNS

Default
CloudFlare_DNS
Value
CloudFlare_DNS
DNS_NAMESERVER_3_IPVariable

CloudFlare_DNS 1.1.1.1

Default
1.1.1.1
Value
1.1.1.1
USE_POSTGRESVariable

Requires DB_ENGINE set to postgres Use a standalone Postgres and set the required Postgres Docker variables manually.

Default
true
Value
false
DB_ENGINEVariable

Choose database backend. If using Postgres, set USE_POSTGRES to true as well.

Default
postgres
Value
sqlite
WEIGHT_LOSSVariable

Weight of packet loss in score (0–1, sum = 1).

Default
-0.6
Value
-0.6
WEIGHT_LATENCYVariable

Weight of latency.

Default
0.15
Value
0.15
WEIGHT_JITTERVariable

Weight of jitter.

Default
0.2
Value
0.2
WEIGHT_DNS_LATENCYVariable

Weight of DNS latency.

Default
0.05
Value
0.05
THRESHOLD_LOSSVariable

Loss % considered “max bad” for scoring.

Default
5
Value
5
THRESHOLD_LATENCYVariable

Latency ms considered “max bad”.

Default
100
Value
100
THRESHOLD_JITTERVariable

Jitter ms considered “max bad”.

Default
30
Value
30
THRESHOLD_DNS_LATENCYVariable

DNS ms considered “max bad”.

Default
100
Value
100
WEB_PORTVariable

This sets the Python web server port. If edited, update Docker NAT ports and WebUI URL.

Default
8080
Value
8080
TailScale Fallback State DirectoryVariable{3}

Fallback container directory for Tailscale state information - Added by Community Applications

Target
CA_TS_FALLBACK_DIR
Default
/data/
Value
/data/