WanWatch is a self-hosted WAN IPv4 monitor with:
- Live dashboard charts (latency, success/error, IP changes)
- Log explorer with filters and pagination
- CSV/JSON exports
- Configurable polling provider + timeout
- Optional webhook on IP change
- Optional Bearer token auth for API access
- Prometheus-style metrics endpoint
- Docker + Docker Compose (recommended), or
- Node.js 20+ with
pnpm
- Copy env file:
cp .env.example .env- Edit
.envand set at minimum:
ADMIN_PASSWORDCOOKIE_SECRET(long random string, at least 32 chars)
- Start services:
docker compose up- Open:
http://localhost:3000
Data is persisted under ./data (default DB path: /app/data/wanwatch.db in containers).
- Install dependencies:
corepack enable
pnpm install- Apply migrations:
pnpm prisma:deploy- Run app and worker in separate terminals:
pnpm dev
pnpm worker:devOr run both with local image builds:
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --buildEnvironment variables are defined in .env.example:
| Variable | Required | Description |
|---|---|---|
ADMIN_PASSWORD |
Yes | Password for web login |
COOKIE_SECRET |
Yes | Session secret (min 32 chars) |
DATABASE_URL |
No | Prisma DB URL (default file:/app/data/wanwatch.db) |
TZ |
No | Timezone for runtime environment |
API_KEY |
No | Enables Bearer token API access when set |
API routes accept either:
- Logged-in web session cookie, or
Authorization: Bearer <API_KEY>(only ifAPI_KEYis set)
Useful endpoints:
GET /api/health(public)GET /api/metrics(public, Prometheus text format)GET /api/statsGET /api/logs?limit=100&cursor=<id>&from=<date>&to=<date>&ok=true|falseGET /api/export.csvGET /api/export.jsonGET /api/settingsPUT /api/settingsPOST /api/poll/request(queue immediate poll)
Example with API key:
curl -H "Authorization: Bearer $API_KEY" http://localhost:3000/api/statsGET /api/metrics exposes:
wanwatch_polls_total{result="ok|error"}wanwatch_ip_changes_totalwanwatch_last_poll_age_secondswanwatch_worker_alivewanwatch_response_ms{quantile="0.5|0.95"}
- Polling runs in
worker/index.tsand writes to SQLite via Prisma. - Manual poll requests are queued via
POST /api/poll/request. - Retention cleanup is automatic based on settings.
- Webhooks fire only on IP change and are guarded against private/internal targets.
- If login succeeds but you are redirected back to
/loginon HTTP deployments, setCOOKIE_SECURE=falsein your env file. - If dashboard shows no data, verify worker logs and hit:
http://localhost:3000/api/logs?limit=5&debug=1(non-production only)- This confirms which DB file the web process is reading.

