Watchdog is a self-hosted monitoring application for websites, containers, and server infrastructure. It runs as a Docker-first application with no external database dependencies — SQLite handles all persistence locally.
Monitoring works in two modes: Dashboard checks run from the watchdog server itself (HTTP reachability, SSL certificates), while Agent checks run on the monitored server via a lightweight Docker container that reports results back to the dashboard.
License: PolyForm Noncommercial License 1.0.0 — source-available, free for personal and non-commercial use. Not OSI Open Source. Commercial use requires explicit permission; contact the maintainer.
| Feature | Details |
|---|---|
| Check types | 14 built-in types: HTTP, HTTP Content, SSL, DNS, TCP, Docker, Docker Exec, disk space, load average, process, log file, file age, file size, Redis, database |
| Agent-based monitoring | Lightweight Docker agent for remote/customer servers — fetches checks from dashboard, pushes results back |
| Alert logic | Email alerts on status transitions only (ok→fail, fail→ok) — no repeated spam |
| No external dependencies | SQLite database, FrankenPHP all-in-one binary — no MySQL, no Redis, no separate web server |
| Async execution | Symfony Messenger for non-blocking check dispatch and email delivery |
| Scheduler | Symfony Scheduler for interval-based and time-based check execution |
| Container | Purpose |
|---|---|
app |
FrankenPHP web server — UI and REST API |
worker |
Messenger consumer — executes dashboard checks, sends alert emails |
scheduler |
Triggers due checks on configured intervals |
agent |
Runs agent-mode checks locally and pushes results to the dashboard |
mailpit |
Local mail catch-all (dev/stage only) |
git clone https://github.com/markus-michalski/watchdog.git /opt/watchdog
cd /opt/watchdog
git checkout $(git tag --sort=-version:refname | head -1)
cp .env.example .env.local
The git checkout line pins you to the latest stable release instead of the main branch (which may contain unreleased changes).
Edit .env.local and fill in:
APP_SECRET=<random 32-char string>
APP_ADMIN_USER=admin
APP_ADMIN_PASSWORD_HASH= # see below
MAILER_DSN=smtp://user:pass@smtp.example.com:587
MAILER_FROM=watchdog@yourdomain.com
DEFAULT_URI=https://watchdog.yourdomain.com
WATCHDOG_AGENT_TOKEN= # set after creating the agent in the UI
docker run --rm dunglas/frankenphp:1-php8.4-alpine php -r \
"echo password_hash('yourpassword', PASSWORD_BCRYPT) . PHP_EOL;"
Bcrypt hashes contain $ characters. Each $ must be doubled to $$ in .env.local because Docker Compose interpolates $ in env files:
# Wrong — Docker Compose mangles the hash
APP_ADMIN_PASSWORD_HASH=$2y$13$abc...
# Correct
APP_ADMIN_PASSWORD_HASH=$$2y$$13$$abc...
The same applies to APP_SECRET if it contains $.
make stage-build
make stage-up
After first login, go to Settings → Agents, create a new agent (e.g. host-server), and copy the generated token into WATCHDOG_AGENT_TOKEN in .env.local. Then restart the agent container:
docker compose -f compose.stage.yml up -d agent
See Agent Setup for details.
cd /opt/watchdog
git fetch --tags
git checkout $(git tag --sort=-version:refname | head -1)
make stage-build
make stage-up
make stage-up runs pending database migrations automatically. Check the CHANGELOG before updating for any manual steps.
Alerts are sent by email on status transitions only:
ok → fail triggers a failure notificationfail → ok triggers a recovery notificationConfigure alert recipients per monitored site in the UI under the client's contact settings.
make stage-up Start containers + run migrations
make stage-down Stop containers
make stage-restart Stop then start
make stage-build Rebuild image without cache
make stage-logs Tail all container logs
make stage-shell Shell into app container
make live-up Start containers + run migrations (live)
make live-update Zero-downtime rebuild + redeploy
make live-logs Tail all container logs
make migrate Run pending migrations
make test PHPUnit
make stan PHPStan level 8
make cs Code style check
make fix Code style autofix
Watchdog is licensed under the PolyForm Noncommercial License 1.0.0. Commercial use requires a separate agreement.
Contributions require signing the Contributor License Agreement. See CONTRIBUTING.md for the development workflow.
Issues and feature requests: GitHub Issues