Getting started
Stand up NetPulse with Docker, create the first admin, wire a probe to a connection, and run the agent — start to first measurement.
Prerequisites
- Docker and Docker Compose
- git
- ~1 GB free disk
- Free local ports: 8080 (dashboard), 9090 (Prometheus), 3000 (Grafana)
Install
Clone the repository and bring the stack up. The task runner is just (brew install just, or use the Nix shell which provides it):
git clone https://github.com/MrHDOLEK/NetPulse.git && cd NetPulse
just install # builds the image, starts containers, runs composer install
just start # docker compose up -djust install does the heavy lifting once; just start is the day-to-day "bring it up" command. Run just to list every recipe.
Platform notes
- On Linux the Justfile uses
docker compose; on macOS it usesdocker-compose. The Justfile auto-detects which is present, so the samejustcommands work either way. - If you hit permission errors talking to the Docker daemon, prefix the command with
sudo.
Develop with Nix (optional)
Prefer to run the PHP toolchain on your host instead of inside Docker? A shell.nix (classic, no flake) provides PHP 8.5, Composer, Mago, the Symfony CLI and just — everything the quality gate and CLI need. The full app stack (nginx, the agent + Ookla CLI, Prometheus, Grafana) still runs in Docker Compose.
1. Install Nix — follow the official installer. On macOS/Linux:
sh <(curl -L https://nixos.org/nix/install)
# then restart your shell2. Enter the shell from the project root:
nix-shell # PHP 8.5 + Composer + Mago + Symfony CLI + just
nix-shell --pure # full isolation from the host
nix-shell --arg php-version 8.4 # choose a PHP version (default: 8.5)
nix-shell --arg with-xdebug true # add Xdebug
type php # show the Nix-provided PHP path (handy for IDE config)3. Inside the shell, install dependencies and run the gate:
composer install # also installs the Mago + Deptrac tools under tools/
just lint # Mago format check + lint
just analyze # Mago static analysis
just deptrac # architecture (0 violations)4. Serve the app from the host (browser access, no Docker) with the bundled Symfony CLI:
cp .env.dist .env # dev env — SQLite at var/netpulse.sqlite
php bin/console importmap:install # fetch the import-map assets
php bin/console tailwind:build # compile the Tailwind CSS
composer migrate # create/upgrade the dev database
symfony server:start # serves on https://127.0.0.1:8000Open the printed URL in your browser — the first visit lands on /setup. Useful flags & commands:
symfony server:start -d # run as a background daemon
symfony server:start --no-tls # plain HTTP instead of HTTPS
symfony server:ca:install # trust the local HTTPS cert (once)
symfony server:log # tail the server logs
symfony server:stop # stop it5. Run the probe agent as a plain host process — it's just a console command (app:agent:run: poll for due work → run Ookla → push results). The Nix shell already bundles the Ookla speedtest CLI (pkgs.ookla-speedtest), so just create a probe and run the agent against the server you started in step 4:
php bin/console app:probe:create "My probe" # prints PROBE_ID + a one-time token
PROBE_ID=<id> PROBE_TOKEN=<token> NETPULSE_API_URL=http://127.0.0.1:8000 \
php bin/console app:agent:run # daemon loop; add --once for a single tickIt reads NETPULSE_API_URL, PROBE_ID, PROBE_TOKEN, OOKLA_BINARY (default speedtest) and AGENT_POLL_INTERVAL (default 60s) from the environment.
Everything on the host (no Docker)
symfony server:start (web + API) + app:agent:run (agent, using the shell's bundled speedtest) runs the whole app on the host. Docker Compose is then only the convenient way to get Prometheus + Grafana (just start).
Build the database
NetPulse is migrations-only — never run doctrine:schema:update. Apply the migrations to build each database.
# DEV database
docker compose exec -T app composer migrate
# TEST database (only needed if you run the test suite)
docker compose exec -T app composer db:testSQLite volume ownership
If you use the named app_sqlite volume, fix its ownership once so the web user can write the SQLite file:
docker compose exec -T app chown -R www-data:www-data varCreate the first admin
NetPulse is single-tenant. Until an admin account exists, every page redirects to /setup.
- Open http://localhost:8080.
- You land on
/setup. Enter an email and a password (minimum 12 characters). - After setup, sign in at http://localhost:8080/login.
CLI alternative
You can create the admin from the console instead:
docker compose exec -T app php bin/console app:user:create --email=ops@example.comPrefer the hidden interactive prompt — omit --password and let the command ask for it.
Add a probe and a connection
A probe is the machine that runs speed tests; a connection is the link you measure on a schedule.
1. Create a probe and capture its id and one-time token (the token is shown once):
docker compose exec -T app php bin/console app:probe:create "Office probe"2. Create a connection bound to that probe:
docker compose exec -T app php bin/console app:connection:create "WAN" \
--probe=<PROBE_ID> \
--schedule-mode=even \
--tests-per-day=288 \
--server-pool=11111,22222A connection with no prior measurement is due immediately, so the agent will pick it up on its first poll.
TIP
Connections can also be created and managed from the dashboard UI — the CLI is handy for scripting and first-run setup.
Run the agent
The agent ships as a dedicated agent Docker service behind the agent compose profile, so it does not start with a plain docker compose up. Pass the probe identity via environment variables and start it under the profile:
PROBE_ID=<PROBE_ID> PROBE_TOKEN=<PROBE_TOKEN> \
docker compose --profile agent up agentThe agent loop:
- Polls
GET /api/v1/probes/<id>/due(authenticated with the probe Bearer token). - Runs the Ookla
speedtestCLI for each due task. - POSTs each result back to the server, where it surfaces on
/metricsand the dashboard.
Run a single tick (handy for testing or ops):
docker compose --profile agent run --rm \
-e PROBE_ID=<PROBE_ID> -e PROBE_TOKEN=<PROBE_TOKEN> \
agent php bin/console app:agent:run --onceVerify the Ookla CLI is present in the image:
docker compose exec -T app speedtest --versionSee your data
Once results start landing:
- Dashboard — http://localhost:8080: Speed / Ping / Loss over 24h / 7d / 30d / 90d, full history at
/history, and a heatmap at/heatmap. - REST API docs — http://localhost:8080/api/doc (Swagger via NelmioApiDoc).
- Prometheus — the
/metricsendpoint, scraped by the bundled Prometheus + Grafana services.
Early-history charts
New tests cluster into a single point on a wide window at first. The chart shows individual points until enough history accumulates to spread out — keep the agent running and the curves fill in.
Run the prebuilt image (GHCR)
Don't want to build from source? The production image — nginx + php-fpm in one container — is published to the GitHub Container Registry on every push to main. Pull and run it:
# a stable random secret (rotating it logs everyone out — keep it)
export APP_SECRET=$(openssl rand -hex 16)
docker run -d --name netpulse \
-p 8080:8080 \
-e APP_SECRET="$APP_SECRET" \
-v netpulse_var:/var/www/var \
ghcr.io/mrhdolek/netpulse:latest…or with the bundled compose file:
docker compose -f compose.prod.yml pull
docker compose -f compose.prod.yml up -dThe container applies migrations on boot and serves the dashboard on http://localhost:8080 — create the first admin at /setup, then add a probe and connection as above. Available tags: latest (from main), sha-<short>, and vX.Y.Z for tagged releases. Storage defaults to SQLite in the var volume; set DATABASE_URL to a PostgreSQL DSN for larger installs.
Grafana dashboard
The Docker Compose stack ships Grafana, provisioned against the Prometheus that scrapes /metrics, with a ready-made NetPulse Overview dashboard. With the stack running, open http://localhost:3000 (default login admin / admin) — the dashboard is already there under Dashboards.
The dashboard JSON lives at .docker/grafana/dashboards/netpulse-overview.json — import it into any Grafana that can read the NetPulse Prometheus metrics.
Next
- Configuration — environment variables and options.
- How it works — scheduling, the agent loop, and the metrics path explained.