docker: run as non-root, add health check, document upaas (closes #147)
check / check (push) Successful in 5s

The runtime image runs as uid 10001, which owns /var/lib/dnswatcher. The
working directory is /, so config loading finds no .env or dnswatcher
config file there; the binary lives in /usr/local/bin. A Docker
HEALTHCHECK probes /.well-known/healthcheck every 10 seconds with busybox
wget, well inside the 60 seconds upaas waits.

Startup now fails with an error naming the data directory when it cannot
be written, instead of running with every save failing. The check creates
the directory if needed and writes and removes the temp file Save uses;
tests cover the create and the write failing.

README gains "Running under upaas": the prod branch, host directory
setup, network and port, environment and health check.

Model: opus-5-5
This commit was merged in pull request #156.
This commit is contained in:
2026-09-28 20:13:37 +02:00
parent 8aaa103956
commit 148e47d9c0
5 changed files with 212 additions and 9 deletions
+49 -3
View File
@@ -508,11 +508,57 @@ docker run -d \
---
## Running under upaas
[upaas](https://git.eeqj.de/sneak/upaas) builds the image from this
repository's `Dockerfile` and runs it. The app needs:
- **Branch:** `prod`. `prod` is cut from `main`, and merging a `main` to
`prod` pull request is a deploy.
- **Volume:** one host directory mounted at `/var/lib/dnswatcher`, where
the state file lives. upaas bind-mounts the host path it is given and
does not create it. The container runs as uid 10001 and does not start
unless it can write there. Create the directory before the first
deploy:
```sh
mkdir -p /path/to/data
chown 10001:10001 /path/to/data
chmod 700 /path/to/data
```
- **Network and port:** the dashboard is unauthenticated and shows every
watched name and recent alert, and upaas publishes every mapped port on
all interfaces of the host
([upaas issue 113](https://git.eeqj.de/sneak/upaas/issues/113)). Add a
port mapping to container port `8080` only if the dashboard should be
public. Otherwise add none: set the app's Docker network in upaas to
your reverse proxy's Docker network, and the proxy reaches the app at
`upaas-` followed by the app name, port `8080`.
- **Required environment:** `DNSWATCHER_TARGETS`, a comma-separated list
of the domains and hostnames to watch. dnswatcher refuses to start
without it.
- **Recommended environment:** at least one notification endpoint
(`DNSWATCHER_SLACK_WEBHOOK`, `DNSWATCHER_MATTERMOST_WEBHOOK`,
`DNSWATCHER_NTFY_TOPIC`); without one, changes show only on the
dashboard. `DNSWATCHER_METRICS_USERNAME` and
`DNSWATCHER_METRICS_PASSWORD` serve `/metrics` behind basic auth.
- **Leave unset:** `DNSWATCHER_DATA_DIR`, which the image sets to
`/var/lib/dnswatcher`, and `PORT`, which defaults to `8080`. Every
setting comes from the environment; the image holds no config file.
- **Health check:** the image's own, which requests
`/.well-known/healthcheck` every 10 seconds. upaas reads the
container's health 60 seconds after a deploy and marks the deploy
failed unless it is `healthy`.
---
## Monitoring Lifecycle
1. **Startup**: Load state from disk. If no state file exists, start
with empty state (first check will establish baseline without
triggering change notifications).
1. **Startup**: Check that the data directory can be written, and exit
with an error naming it if not. Load state from disk. If no state
file exists, start with empty state (first check will establish
baseline without triggering change notifications).
2. **Initial check**: Immediately perform all DNS, port, and TLS checks
on startup.
3. **Periodic checks** (DNS always runs first):