Daily monitoring for domain expiry, trademark expiry, and DNS/SSL drift. One Python service, one systemd timer, one daily summary email. Replaces three separate paid SaaS tools.
Born from running 40+ domains and 12+ trademarks at Die Netzhandwerker. Combines patterns from three internal tools (Verfallswache, Markenwacht, DailyWatch) into one open package.
| Check | What it catches | Tool replaced |
|---|---|---|
| Domain WHOIS expiry | A domain you forgot is expiring next month | DomainTools, $50/mo |
| Trademark expiry (DPMAregister, EUIPO, USPTO) | IP-protected name expiring or contested | Markify, $30/mo |
| SSL certificate expiry | A subdomain cert silently expired | UptimeRobot, $7/mo |
| DNS record drift | A registrar changed your nameservers or your DNSSEC | (DIY) |
| New TXT records | Someone added an SPF/DMARC entry behind your back | (DIY) |
| Whois owner change | Domain was transferred or hijacked | (DIY) |
You get one mail per day (default 06:00 UTC) summarizing what changed.
git clone https://github.com/netzhandwerker/domain-watchtower.git
cd domain-watchtower
sudo ./install.shEdit /etc/domain-watchtower/watchlist.yml:
domains:
- name: example.com
checks: [expiry, ssl, dns, txt]
alert_days: [60, 30, 14, 7, 3, 1]
- name: example.de
checks: [expiry, ssl]
alert_days: [60, 14, 1]
trademarks:
- name: "Example"
register: dpma # dpma | euipo | uspto
id: "302024123456"
alert_days: [180, 90, 30]
alert:
to: ops@example.com
smtp_host: localhost
smtp_port: 25Enable the daily run:
sudo systemctl enable --now domain-watchtower.timerYou'll get the first report tomorrow at 06:00 UTC.
┌─────────────────────────────┐
│ /etc/domain-watchtower/ │
│ watchlist.yml │
└────────────┬────────────────┘
│
┌────────────▼────────────────┐
│ watchtower.py │
│ │
│ ┌──────────────────────┐ │
│ │ checks/whois.py │──┼──→ python-whois
│ │ checks/ssl.py │──┼──→ openssl s_client
│ │ checks/dns.py │──┼──→ dnspython
│ │ checks/txt.py │──┼──→ dnspython
│ │ checks/trademark.py │──┼──→ DPMA/EUIPO/USPTO
│ └──────────────────────┘ │
│ │
│ State diff against │
│ /var/lib/domain-watch... │
└────────────┬────────────────┘
│
┌────────────▼────────────────┐
│ Daily summary mail │
│ (only sent if changes) │
└─────────────────────────────┘
State is kept in /var/lib/domain-watchtower/state.json. On first run there's no baseline — second run onwards detects diffs.
Subject: [domain-watchtower] 3 alerts on 2026-05-20
DOMAIN EXPIRY
example.de expires in 14 days (2026-06-03)
oldsite.com expires in 3 days (2026-05-23) *** URGENT ***
TRADEMARK EXPIRY
"Example" (DPMA 302024123456) expires in 87 days
SSL EXPIRY
api.example.com cert expires in 9 days (2026-05-29)
DNS / TXT CHANGES
example.com NEW TXT record: "v=spf1 include:_spf.mailerlite.com ~all"
(was: "v=spf1 include:_spf.google.com ~all")
DRIFT
example.de nameservers changed:
old: ns1.registrar-a.com, ns2.registrar-a.com
new: ns1.registrar-b.com, ns2.registrar-b.com
See examples/watchlist.full.yml for all available options.
- Not a registrar — it watches your domains, you renew them yourself
- Not a legal monitor — for actual trademark litigation use a real IP lawyer
- Not real-time — daily resolution by design (cron-friendly)
- Not a registrar webhook handler — pull-based via public WHOIS / RDAP
The repo combines three previously-separate tools:
- Verfallswache — domain expiry monitoring with daily alerts
- Markenwacht — German DPMA trademark expiry monitor (extended to EUIPO + USPTO)
- DailyWatch — DNS/SSL/TXT drift detection with state diff
All three are now subcommands of a single CLI.
MIT.
Built by Daniel Wesseling at Die Netzhandwerker. In daily production use against 40+ domains and 12+ trademarks since 2025.