nexpulse

nexpulse

Docker app from DerKezorm's Repository

Overview

nexpulse is a speed test tracker for the homelab. It measures your internet connection on a schedule, shows every test live on a gauge, keeps the history with charts and averages, and tells you over ntfy, Gotify or a webhook when the line is slower than your plan. It measures with Cloudflare out of the box, with public or your own LibreSpeed servers, and optionally with Ookla. Schedules can run every few minutes, daily, by cron, or several times a day at random times to find the busy hours. An API with read or run keys feeds dashboards like nexdeck or Home Assistant. The interface is available in English and German.

nexpulse

A speed test tracker for your homelab. Measures on a schedule, shows every test live on a gauge, keeps the history and tells you when your line is slower than what you pay for.

Status: early. A small side project, part of the nex apps (Nexview and friends).

Live view: gauge with ping, download and upload, the last result and the connection

The screenshots show a throwaway instance with made-up data.

What it does

  • Live view with a gauge: ping, download and upload as they happen, whether you started the test or a schedule did.
  • Three sources: Cloudflare (works right away), LibreSpeed (public servers or your own) and, if you activate it yourself, Ookla. See below.
  • Everything measurable: download, upload, ping, jitter, lowest and highest ping, latency under load (bufferbloat), and packet loss where the source measures it.
  • Schedules: every n minutes, once a day, a cron expression, or n times a day at random times: the day is split into n equal parts with one test at a random minute in each, drawn anew every day, so over a few weeks every hour is covered and busy times show up. All of them can be limited to weekdays and a time window. Tests never overlap.
  • History with charts, averages and a CSV export.
  • Your plan: enter what your provider promises, and nexpulse marks tests below a threshold of it.
  • Alerts via ntfy, Gotify or a webhook: slow test, high ping, failed test.
  • API for dashboards like nexdeck or Home Assistant, with keys that can only read or also start tests.
  • No accounts. Optionally one password for the interface. The API always needs a key.
  • German and English, dark and light.

History with averages, the plan line and a chart per direction

Sources

Source Server choice Packet loss Notes
Cloudflare automatic (nearest data center) no The same endpoints as speed.cloudflare.com. Cloudflare refuses tests that come too often; one an hour is plenty.
LibreSpeed automatic, a public server or your own no Automatic pings all servers, tries the nearest ones briefly and takes the fastest.
Ookla automatic or any nearby server yes Not included. See below.

About Ookla

nexpulse does not ship any Ookla software. If you activate Ookla in the settings, nexpulse downloads the official Speedtest CLI from Ookla into your data folder.

Ookla's license does not cover this use. It allows the CLI only for personal, non-commercial use on a single personal computer, and forbids running it on a device that other devices can reach over the network, or on routers, modems or other devices that are not personal computers. A container in a homelab falls under that. nexpulse says so before you activate it; if you do, it is your decision and your risk.

Schedules, including one with random times

Install

services:
  nexpulse:
    image: ghcr.io/derkezorm/nexpulse:latest
    container_name: nexpulse
    restart: unless-stopped
    ports:
      - "8440:8000"
    volumes:
      - ./data:/data
    environment:
      PUID: 1000
      PGID: 1000
      TZ: Europe/Berlin

Then open http://<your-host>:8440. The full example with every option is in docker-compose.yml.

Forgot the password? This turns it off:

docker exec nexpulse python -m app.cli remove-password

API

Create a key under Settings > Access and API, then send it as X-Api-Key or as Authorization: Bearer.

Method Path Access
GET /api/v1/me read (any key: name, access, whether it may start tests)
GET /api/v1/status read
GET /api/v1/latest read
GET /api/v1/results?from=…&to=…&source=… read
GET /api/v1/stats?range=24h|7d|30d|90d|all read
POST /api/v1/tests with {"source": "cloudflare"} (or librespeed, ookla) run
GET /api/v1/tests/{id} read

The full reference is at /api/docs on your instance.

Development

Part Command
Backend cd backend && python -m venv .venv && .venv/Scripts/pip install -r requirements-dev.txt, then python -m uvicorn app.main:app --port 8440
Frontend npm --prefix frontend install, then npm --prefix frontend run dev (port 5440, proxies /api to 8440)
Checks python -m ruff check app tests, python -m pytest -q in backend; npm run lint, npm test, npm run build in frontend

License

GNU AGPL v3.0. Cloudflare, LibreSpeed, Ookla and Speedtest are names of their owners; nexpulse is not affiliated with them.

Install Nexpulse on Unraid in a few clicks.

Find Nexpulse 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 Nexpulse Review the template variables and paths Click Install

Related apps

Details

Repository
ghcr.io/derkezorm/nexpulse
Last Updated2026-09-21
First Seen2026-09-21

Runtime arguments

Web UI
http://[IP]:[PORT:8000]/
Network
bridge
Shell
sh
Privileged
false

Template configuration

WebUI PortPorttcp

Port you reach nexpulse at. Container port: 8000

Target
8000
Default
8440
Value
8440
Data (Container Path: /data)Pathrw

Holds the SQLite database with all results and the key that signs sessions. Keep it on a local disk, not on a network share.

Target
/data
Default
/mnt/user/appdata/nexpulse
Value
/mnt/user/appdata/nexpulse
PUIDVariable

User id that should own the files in /data. 99 is the Unraid default.

Default
99
Value
99
PGIDVariable

Group id that should own the files in /data. 100 is the Unraid default.

Default
100
Value
100
Time ZoneVariable

Schedules run at the wall clock time of this zone, for example Europe/Berlin. Leave blank to pick the zone later in the settings; until then nexpulse uses UTC.

Target
TZ
Container Port (host networking only)Variable

Leave this empty unless you switched Network Type to Host. In bridge mode the WebUI Port above already does the job and setting this will break it, because the mapping still points at 8000. On host networking the port inside the container is the port on your server, so use this to move nexpulse off 8000 if something else is already there.

Target
NEXPULSE_PORT
Secret KeyVariable

Signs sessions and encrypts stored notification tokens. Leave blank: nexpulse creates one on first start and keeps it in /data/secret.key.

Target
NEXPULSE_SECRET_KEY
Secure CookieVariable

Only matters with a password. auto marks the session cookie Secure when the request arrived over https. Use on only if a proxy terminates TLS and forwards plain http, and never if nexpulse should also be reachable over http, or nobody can sign in. off never marks it.

Target
NEXPULSE_COOKIE_SECURE
Default
auto
Value
auto