@sneak these are the parts of putting dnswatcher on fsn1app1 that need your access or your call. The repo-side work is tracked separately and does not wait on this.
Decision: which branch does upaas deploy from?
#78 (closed) planned a prod branch fed by periodic main to prod PRs. Since then this repo gained next, so main already only ever receives a reviewed milestone that you merged yourself.
Deploy from main. Your merge of the milestone PR is the deploy. No third branch.
Deploy from prod. One more PR per deploy; lets main run ahead of production.
Recommendation: option 1. main is already gated by you, and a third branch adds a step without adding a check. The README deploy section will say main unless you say otherwise.
Needs your access
Create the upaas app on fsn1app1: repo URL, deploy key, webhook, branch as decided above.
Volume: a host directory mounted at /var/lib/dnswatcher.
Environment: DNSWATCHER_TARGETS (the real list of domains and hostnames you want watched), and the notification endpoints (DNSWATCHER_SLACK_WEBHOOK, DNSWATCHER_MATTERMOST_WEBHOOK, DNSWATCHER_NTFY_TOPIC), plus DNSWATCHER_METRICS_USERNAME / DNSWATCHER_METRICS_PASSWORD if you want /metrics.
Whether the dashboard is exposed publicly. It is unauthenticated by design and shows every watched name and recent alert.
What should be on main before the first deploy
The two notification types that cannot fire today (#104, #105) and the security headers (#98). It runs fine without them; it just watches less than the README says.
Model: fable-5-1
@sneak these are the parts of putting dnswatcher on `fsn1app1` that need your access or your call. The repo-side work is tracked separately and does not wait on this.
## Decision: which branch does upaas deploy from?
https://git.eeqj.de/sneak/dnswatcher/issues/78 (closed) planned a `prod` branch fed by periodic `main` to `prod` PRs. Since then this repo gained `next`, so `main` already only ever receives a reviewed milestone that you merged yourself.
1. **Deploy from `main`.** Your merge of the milestone PR is the deploy. No third branch.
2. **Deploy from `prod`.** One more PR per deploy; lets `main` run ahead of production.
Recommendation: option 1. `main` is already gated by you, and a third branch adds a step without adding a check. The README deploy section will say `main` unless you say otherwise.
## Needs your access
- Create the upaas app on `fsn1app1`: repo URL, deploy key, webhook, branch as decided above.
- Volume: a host directory mounted at `/var/lib/dnswatcher`.
- Environment: `DNSWATCHER_TARGETS` (the real list of domains and hostnames you want watched), and the notification endpoints (`DNSWATCHER_SLACK_WEBHOOK`, `DNSWATCHER_MATTERMOST_WEBHOOK`, `DNSWATCHER_NTFY_TOPIC`), plus `DNSWATCHER_METRICS_USERNAME` / `DNSWATCHER_METRICS_PASSWORD` if you want `/metrics`.
- Whether the dashboard is exposed publicly. It is unauthenticated by design and shows every watched name and recent alert.
## What should be on `main` before the first deploy
The two notification types that cannot fire today (https://git.eeqj.de/sneak/dnswatcher/issues/104, https://git.eeqj.de/sneak/dnswatcher/issues/105) and the security headers (https://git.eeqj.de/sneak/dnswatcher/issues/98). It runs fine without them; it just watches less than the README says.
Model: fable-5-1
clawbot
added this to the 1.0 milestone 2026-09-21 09:18:32 +02:00
sneak
was assigned by clawbot2026-09-21 09:18:32 +02:00
The deploy-branch question here is settled by sneak's standing ruling of 2026-08-30: upaas deploys each app from a prod branch cut from main, so releases stay independent of what the instances run, and a merged main to prod PR is a deploy. Creating prod and the repo-side readiness are ours, queued for when dnswatcher resumes (paused under the current priority rule). What stays with sneak here is only the setup on fsn1app1 once upaas runs there (sneak/upaas#181).
Model: opus-5-5
The deploy-branch question here is settled by sneak's standing ruling of 2026-08-30: upaas deploys each app from a `prod` branch cut from `main`, so releases stay independent of what the instances run, and a merged `main` to `prod` PR is a deploy. Creating `prod` and the repo-side readiness are ours, queued for when dnswatcher resumes (paused under the current priority rule). What stays with sneak here is only the setup on fsn1app1 once upaas runs there (https://git.eeqj.de/sneak/upaas/issues/181).
Model: opus-5-5
@sneak here is what the upaas app needs, read from the config code on next, which is what you will be deploying.
Environment variables
Only DNSWATCHER_TARGETS is required. A value dnswatcher cannot read (for example DNSWATCHER_DNS_INTERVAL=1d or DNSWATCHER_DEBUG=yes) stops it at startup with an error naming the variable. An empty value is the same as not setting it.
Variable
Required
Default
What it does
Example
DNSWATCHER_TARGETS
yes; it will not start without it
none
Comma-separated list of the names to watch. Put both domains (example.com) and hostnames (www.example.com) here; it works out which is which by itself.
sneak.berlin,www.sneak.berlin,git.eeqj.de
DNSWATCHER_NTFY_TOPIC
no
none
Sends alerts to this ntfy topic. Give the full URL.
https://ntfy.sh/my-dns-alerts
DNSWATCHER_SLACK_WEBHOOK
no
none
Sends alerts to this Slack incoming webhook URL.
https://hooks.slack.com/services/T000/B000/XXXX
DNSWATCHER_MATTERMOST_WEBHOOK
no
none
Sends alerts to this Mattermost incoming webhook URL.
https://mattermost.example.com/hooks/xxxx
DNSWATCHER_SEND_TEST_NOTIFICATION
no
false
When true, sends one "startup complete" message to every alert destination above after the first round of checks, on every start. This is a quick way to see that alerts arrive.
true
DNSWATCHER_SKIP_RECORD_NOTIFICATIONS
no
none
Names from DNSWATCHER_TARGETS that should get no alert when their DNS records change or their nameservers disagree. They are still checked and still shown on the dashboard. A name that is not in DNSWATCHER_TARGETS stops startup.
www.sneak.berlin
DNSWATCHER_DNS_INTERVAL
no
1h
How often DNS records and ports 80 and 443 are checked. Write a number and a unit s, m or h. There is no unit for days, so write 24h.
30m
DNSWATCHER_TLS_INTERVAL
no
12h
How often TLS certificates are checked, in the same format.
6h
DNSWATCHER_TLS_EXPIRY_WARNING
no
7
How many days before a certificate expires to start warning about it.
14
DNSWATCHER_METRICS_USERNAME
no
none
Turns on /metrics (Prometheus) behind a username and password. If this is not set, /metrics does not exist.
prometheus
DNSWATCHER_METRICS_PASSWORD
yes, if the username is set
none
The password for /metrics. Setting the username without a password stops startup.
a long random string
DNSWATCHER_SENTRY_DSN
no
none
Reports crashes in web requests to Sentry. A value Sentry cannot read stops startup.
https://key@o0.ingest.sentry.io/0
DNSWATCHER_DEBUG
no
false
Turns on debug logging.
true
DNSWATCHER_MAINTENANCE_MODE
no
false
Only changes one field in the health check reply and does nothing else. Leave it unset.
DNSWATCHER_DATA_DIR
no, leave unset
/var/lib/dnswatcher
The directory for the state file. The image already sets it.
PORT
no, leave unset
8080
The port the web server listens on inside the container.
With none of the three alert destinations set, dnswatcher still runs, but changes only show up on the dashboard.
Container
Volume: mount a host directory at /var/lib/dnswatcher. It holds one file, state.json. The container starts as root only to hand that directory to its own user (uid 10001), then runs as that user.
Port: the container listens on 8080.
Health check: the path is /.well-known/healthcheck. The image already checks it every 10 seconds, so there is nothing to set up for it in upaas.
No login: the dashboard (/) and /api/v1/status have no login, and they show every watched name and the recent alerts. upaas publishes a mapped port on every network interface of the host (sneak/upaas#113), so only map port 8080 if you want the dashboard public. Otherwise map no port and put the app on your reverse proxy's Docker network. The proxy then reaches it at upaas- followed by the app name, port 8080. Every reply carries Strict-Transport-Security, so serve it only under an HTTPS hostname.
Network: dnswatcher sends its DNS queries (port 53) straight to the root servers and to each domain's own nameservers. It never uses a local resolver. It also connects out on ports 80 and 443 to check ports and certificates. The image build runs the tests, and they query the real DNS too. Your check on fsn1app1 (#301 (comment)) shows its network passes DNS through untouched. A network that answers DNS queries itself makes the build and dnswatcher stop with "this network intercepts DNS queries" (#300).
Build time: the first build takes a few minutes, because it runs the linter and the tests before building.
What prod holds, and how to deploy
prod and main are both at e77e206, from 9 September. That is 92 commits behind next, where the release candidate (v1.0.0-rc3) is. Do not deploy prod as it stands: the image built from that commit exits at startup. next fixes this.
Once main has moved, I will open a pull request from main into prod, titled "main into prod", assigned to you, to be merged as a squash. Merging that pull request is the deploy, because upaas builds from prod.
Model: opus-5-5
@sneak here is what the upaas app needs, read from the config code on `next`, which is what you will be deploying.
## Environment variables
Only `DNSWATCHER_TARGETS` is required. A value dnswatcher cannot read (for example `DNSWATCHER_DNS_INTERVAL=1d` or `DNSWATCHER_DEBUG=yes`) stops it at startup with an error naming the variable. An empty value is the same as not setting it.
| Variable | Required | Default | What it does | Example |
| --- | --- | --- | --- | --- |
| `DNSWATCHER_TARGETS` | **yes**; it will not start without it | none | Comma-separated list of the names to watch. Put both domains (`example.com`) and hostnames (`www.example.com`) here; it works out which is which by itself. | `sneak.berlin,www.sneak.berlin,git.eeqj.de` |
| `DNSWATCHER_NTFY_TOPIC` | no | none | Sends alerts to this ntfy topic. Give the full URL. | `https://ntfy.sh/my-dns-alerts` |
| `DNSWATCHER_SLACK_WEBHOOK` | no | none | Sends alerts to this Slack incoming webhook URL. | `https://hooks.slack.com/services/T000/B000/XXXX` |
| `DNSWATCHER_MATTERMOST_WEBHOOK` | no | none | Sends alerts to this Mattermost incoming webhook URL. | `https://mattermost.example.com/hooks/xxxx` |
| `DNSWATCHER_SEND_TEST_NOTIFICATION` | no | `false` | When `true`, sends one "startup complete" message to every alert destination above after the first round of checks, on every start. This is a quick way to see that alerts arrive. | `true` |
| `DNSWATCHER_SKIP_RECORD_NOTIFICATIONS` | no | none | Names from `DNSWATCHER_TARGETS` that should get no alert when their DNS records change or their nameservers disagree. They are still checked and still shown on the dashboard. A name that is not in `DNSWATCHER_TARGETS` stops startup. | `www.sneak.berlin` |
| `DNSWATCHER_DNS_INTERVAL` | no | `1h` | How often DNS records and ports 80 and 443 are checked. Write a number and a unit `s`, `m` or `h`. There is no unit for days, so write `24h`. | `30m` |
| `DNSWATCHER_TLS_INTERVAL` | no | `12h` | How often TLS certificates are checked, in the same format. | `6h` |
| `DNSWATCHER_TLS_EXPIRY_WARNING` | no | `7` | How many days before a certificate expires to start warning about it. | `14` |
| `DNSWATCHER_METRICS_USERNAME` | no | none | Turns on `/metrics` (Prometheus) behind a username and password. If this is not set, `/metrics` does not exist. | `prometheus` |
| `DNSWATCHER_METRICS_PASSWORD` | yes, if the username is set | none | The password for `/metrics`. Setting the username without a password stops startup. | a long random string |
| `DNSWATCHER_SENTRY_DSN` | no | none | Reports crashes in web requests to Sentry. A value Sentry cannot read stops startup. | `https://key@o0.ingest.sentry.io/0` |
| `DNSWATCHER_DEBUG` | no | `false` | Turns on debug logging. | `true` |
| `DNSWATCHER_MAINTENANCE_MODE` | no | `false` | Only changes one field in the health check reply and does nothing else. Leave it unset. | |
| `DNSWATCHER_DATA_DIR` | no, leave unset | `/var/lib/dnswatcher` | The directory for the state file. The image already sets it. | |
| `PORT` | no, leave unset | `8080` | The port the web server listens on inside the container. | |
With none of the three alert destinations set, dnswatcher still runs, but changes only show up on the dashboard.
## Container
- **Volume:** mount a host directory at `/var/lib/dnswatcher`. It holds one file, `state.json`. The container starts as root only to hand that directory to its own user (uid 10001), then runs as that user.
- **Port:** the container listens on `8080`.
- **Health check:** the path is `/.well-known/healthcheck`. The image already checks it every 10 seconds, so there is nothing to set up for it in upaas.
- **No login:** the dashboard (`/`) and `/api/v1/status` have no login, and they show every watched name and the recent alerts. upaas publishes a mapped port on every network interface of the host (https://git.eeqj.de/sneak/upaas/issues/113), so only map port `8080` if you want the dashboard public. Otherwise map no port and put the app on your reverse proxy's Docker network. The proxy then reaches it at `upaas-` followed by the app name, port `8080`. Every reply carries `Strict-Transport-Security`, so serve it only under an HTTPS hostname.
- **Network:** dnswatcher sends its DNS queries (port 53) straight to the root servers and to each domain's own nameservers. It never uses a local resolver. It also connects out on ports 80 and 443 to check ports and certificates. The image build runs the tests, and they query the real DNS too. Your check on fsn1app1 (https://git.eeqj.de/sneak/dnswatcher/pulls/301#issuecomment-144958) shows its network passes DNS through untouched. A network that answers DNS queries itself makes the build and dnswatcher stop with "this network intercepts DNS queries" (https://git.eeqj.de/sneak/dnswatcher/issues/300).
- **Build time:** the first build takes a few minutes, because it runs the linter and the tests before building.
## What `prod` holds, and how to deploy
`prod` and `main` are both at `e77e206`, from 9 September. That is 92 commits behind `next`, where the release candidate (`v1.0.0-rc3`) is. Do not deploy `prod` as it stands: the image built from that commit exits at startup. `next` fixes this.
1. Merge https://git.eeqj.de/sneak/dnswatcher/pulls/152 (`next` into `main`).
2. Once `main` has moved, I will open a pull request from `main` into `prod`, titled "main into prod", assigned to you, to be merged as a squash. Merging that pull request is the deploy, because upaas builds from `prod`.
Model: opus-5-5
@sneak merging #303 (main into prod, assigned to you) is the deploy. Merge it as a squash. Set up the upaas app with the settings above before you merge, so the first build deploys a working container.
Model: opus-5-5
@sneak merging https://git.eeqj.de/sneak/dnswatcher/pulls/303 (`main` into `prod`, assigned to you) is the deploy. Merge it as a squash. Set up the upaas app with the settings above before you merge, so the first build deploys a working container.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
@sneak these are the parts of putting dnswatcher on
fsn1app1that need your access or your call. The repo-side work is tracked separately and does not wait on this.Decision: which branch does upaas deploy from?
#78 (closed) planned a
prodbranch fed by periodicmaintoprodPRs. Since then this repo gainednext, somainalready only ever receives a reviewed milestone that you merged yourself.main. Your merge of the milestone PR is the deploy. No third branch.prod. One more PR per deploy; letsmainrun ahead of production.Recommendation: option 1.
mainis already gated by you, and a third branch adds a step without adding a check. The README deploy section will saymainunless you say otherwise.Needs your access
fsn1app1: repo URL, deploy key, webhook, branch as decided above./var/lib/dnswatcher.DNSWATCHER_TARGETS(the real list of domains and hostnames you want watched), and the notification endpoints (DNSWATCHER_SLACK_WEBHOOK,DNSWATCHER_MATTERMOST_WEBHOOK,DNSWATCHER_NTFY_TOPIC), plusDNSWATCHER_METRICS_USERNAME/DNSWATCHER_METRICS_PASSWORDif you want/metrics.What should be on
mainbefore the first deployThe two notification types that cannot fire today (#104, #105) and the security headers (#98). It runs fine without them; it just watches less than the README says.
Model: fable-5-1
clawbot referenced this issue2026-09-21 09:44:48 +02:00
clawbot referenced this issue2026-09-21 10:01:02 +02:00
The deploy-branch question here is settled by sneak's standing ruling of 2026-08-30: upaas deploys each app from a
prodbranch cut frommain, so releases stay independent of what the instances run, and a mergedmaintoprodPR is a deploy. Creatingprodand the repo-side readiness are ours, queued for when dnswatcher resumes (paused under the current priority rule). What stays with sneak here is only the setup on fsn1app1 once upaas runs there (sneak/upaas#181).Model: opus-5-5
yes, option 2. upaas is up now and i'm ready to set up the autodeployment. what settings do i need to set in env?
@sneak here is what the upaas app needs, read from the config code on
next, which is what you will be deploying.Environment variables
Only
DNSWATCHER_TARGETSis required. A value dnswatcher cannot read (for exampleDNSWATCHER_DNS_INTERVAL=1dorDNSWATCHER_DEBUG=yes) stops it at startup with an error naming the variable. An empty value is the same as not setting it.DNSWATCHER_TARGETSexample.com) and hostnames (www.example.com) here; it works out which is which by itself.sneak.berlin,www.sneak.berlin,git.eeqj.deDNSWATCHER_NTFY_TOPIChttps://ntfy.sh/my-dns-alertsDNSWATCHER_SLACK_WEBHOOKhttps://hooks.slack.com/services/T000/B000/XXXXDNSWATCHER_MATTERMOST_WEBHOOKhttps://mattermost.example.com/hooks/xxxxDNSWATCHER_SEND_TEST_NOTIFICATIONfalsetrue, sends one "startup complete" message to every alert destination above after the first round of checks, on every start. This is a quick way to see that alerts arrive.trueDNSWATCHER_SKIP_RECORD_NOTIFICATIONSDNSWATCHER_TARGETSthat should get no alert when their DNS records change or their nameservers disagree. They are still checked and still shown on the dashboard. A name that is not inDNSWATCHER_TARGETSstops startup.www.sneak.berlinDNSWATCHER_DNS_INTERVAL1hs,morh. There is no unit for days, so write24h.30mDNSWATCHER_TLS_INTERVAL12h6hDNSWATCHER_TLS_EXPIRY_WARNING714DNSWATCHER_METRICS_USERNAME/metrics(Prometheus) behind a username and password. If this is not set,/metricsdoes not exist.prometheusDNSWATCHER_METRICS_PASSWORD/metrics. Setting the username without a password stops startup.DNSWATCHER_SENTRY_DSNhttps://key@o0.ingest.sentry.io/0DNSWATCHER_DEBUGfalsetrueDNSWATCHER_MAINTENANCE_MODEfalseDNSWATCHER_DATA_DIR/var/lib/dnswatcherPORT8080With none of the three alert destinations set, dnswatcher still runs, but changes only show up on the dashboard.
Container
/var/lib/dnswatcher. It holds one file,state.json. The container starts as root only to hand that directory to its own user (uid 10001), then runs as that user.8080./.well-known/healthcheck. The image already checks it every 10 seconds, so there is nothing to set up for it in upaas./) and/api/v1/statushave no login, and they show every watched name and the recent alerts. upaas publishes a mapped port on every network interface of the host (sneak/upaas#113), so only map port8080if you want the dashboard public. Otherwise map no port and put the app on your reverse proxy's Docker network. The proxy then reaches it atupaas-followed by the app name, port8080. Every reply carriesStrict-Transport-Security, so serve it only under an HTTPS hostname.What
prodholds, and how to deployprodandmainare both ate77e206, from 9 September. That is 92 commits behindnext, where the release candidate (v1.0.0-rc3) is. Do not deployprodas it stands: the image built from that commit exits at startup.nextfixes this.nextintomain).mainhas moved, I will open a pull request frommainintoprod, titled "main into prod", assigned to you, to be merged as a squash. Merging that pull request is the deploy, because upaas builds fromprod.Model: opus-5-5
@sneak merging #303 (
mainintoprod, assigned to you) is the deploy. Merge it as a squash. Set up the upaas app with the settings above before you merge, so the first build deploys a working container.Model: opus-5-5