Improve README SEO and screenshots

This commit is contained in:
StasonJatham committed 2026-05-20 14:42:33 +02:00
1 parent f2983da713
commit 7ce4f6816f
6 files changed
+116 -32

No files matched your search

+100 -32
View File
@@ -1,23 +1,70 @@
# router-dyndns
# RouterPulse
`router-dyndns` is a small self-hosted DynDNS service for FRITZ!Box and compatible routers. It provides FRITZ!Box-ready update URLs, publishes A/AAAA records through Cloudflare or RFC 2136, and keeps the operational footprint simple enough for a small VPS.
**Self-hosted DynDNS for FRITZ!Box, OpenWrt, pfSense, OPNsense, UniFi, MikroTik, and any router that can call a custom DDNS update URL.**
## Status
RouterPulse is the friendly name for `router-dyndns`: a small FastAPI Dynamic DNS provider you can run on your own VPS. It gives users FRITZ!Box-ready DynDNS credentials, keeps public A/AAAA records updated through Cloudflare or RFC 2136, and supports verified custom domains with DNS TXT ownership checks.
This project is suitable for self-hosting and private/public beta use when a real DNS backend is configured. For a larger free public service, add external abuse controls, monitoring, and a backup/restore process before launch.
If you want a lightweight DuckDNS / Dynu / No-IP style service that you control yourself, RouterPulse is built for that use case.
## Features
![RouterPulse self-hosted DynDNS home screen](docs/screenshots/home.png)
- FastAPI application with Swagger UI, ReDoc, and OpenAPI schema.
- Anonymous provider-owned hostnames using cryptographically random update and management URLs.
- Login-gated custom-domain verification with DNS TXT challenges.
- FRITZ!Box-compatible update endpoint and a JSON API under `/api/v1`.
- SQLite persistence with WAL mode and per-IP rate limiting.
- Cloudflare and RFC 2136 DNS publishing.
- Publish-before-store update behavior, with failed DNS updates logged and rejected.
- Per-host update locking inside one server process.
- Safe trusted-proxy handling for `X-Forwarded-For`.
- Docker Compose and Caddy examples.
## Why RouterPulse?
- **Self-hosted Dynamic DNS provider** for home labs, small ISPs, communities, and private infrastructure.
- **FRITZ!Box compatible**: copy the generated `Update-URL`, `Domainnamen`, `Benutzername`, and `Kennwort` straight into the German FRITZ!Box DynDNS form.
- **No account required for basic hostnames**: generate a cryptographically random provider hostname and private management link.
- **Custom domain support**: users enter a domain, add a DNS TXT record, then press a button to verify ownership before credentials are issued.
- **Real DNS publishing** through **Cloudflare** or **RFC 2136** with A and AAAA support.
- **Small-server friendly**: FastAPI, SQLite WAL, simple Docker deployment, and no heavy background platform.
- **API first**: OpenAPI, Swagger UI, ReDoc, and JSON endpoints under `/api/v1`.
## Screenshots
### Dashboard
![RouterPulse dashboard for router hostnames](docs/screenshots/dashboard.png)
### Generate Router Credentials
![Generated FRITZ!Box DynDNS credentials](docs/screenshots/generated-credentials.png)
### Verify a Custom Domain
![DNS TXT verification flow for custom domains](docs/screenshots/domain-verification.png)
## What It Does
RouterPulse lets you host your own managed DynDNS service:
1. A user opens your RouterPulse URL.
2. They generate a random hostname like `a1b2c3d4.ddns.example.net`, or verify their own domain with a TXT record.
3. RouterPulse shows the exact router settings for the DynDNS form.
4. The router periodically calls the update URL with its current public IPv4/IPv6 address.
5. RouterPulse publishes the matching DNS records through your configured DNS backend.
That means users always have a current hostname for VPN, remote access, self-hosted apps, home servers, NAS devices, and lab networks.
## Best Use Cases
- Self-hosted DynDNS for FRITZ!Box routers.
- Free DDNS service for friends, customers, a community, or a home lab.
- Dynamic DNS for IPv4 and IPv6 home internet connections.
- Custom domain DDNS with DNS TXT verification.
- Lightweight Cloudflare DDNS provider with a web UI.
- RFC 2136 / TSIG DynDNS frontend for BIND, Knot, PowerDNS, or compatible DNS servers.
## Feature Overview
| Area | Support |
| --- | --- |
| Router update URLs | FRITZ!Box custom provider URL, `/u/<slug>`, and `/nic/update` compatibility |
| DNS records | A and AAAA |
| DNS providers | Cloudflare API and RFC 2136 dynamic DNS |
| Custom domains | TXT challenge verification before hostname creation |
| Persistence | SQLite with WAL mode |
| API | FastAPI, OpenAPI, Swagger UI, ReDoc |
| Auth model | Anonymous magic links for simple hostnames, login workspace for custom domains |
| Operations | Rate limiting, update logs, cleanup jobs, Docker, Caddy example |
## Quick Start
@@ -43,16 +90,16 @@ router-dyndns serve --host 127.0.0.1 --port 8080
Open:
- Customer UI: `http://localhost:8080/`
- Web UI: `http://localhost:8080/`
- Admin UI: `http://localhost:8080/admin`
- Swagger UI: `http://localhost:8080/docs`
- ReDoc: `http://localhost:8080/redoc`
Admin auth uses HTTP Basic auth. The username can be any value; the password is `DDNS_ADMIN_PASSWORD`.
## Production Configuration
## Production Setup
For an internet-facing service, configure HTTPS at your reverse proxy and require a DNS backend:
For an internet-facing DynDNS service, run behind HTTPS and require a real DNS backend:
```bash
export DDNS_PUBLIC_BASE_URL='https://ddns.example.net'
@@ -63,7 +110,7 @@ export DDNS_DNS_ZONES='ddns.example.net'
export DDNS_TRUSTED_PROXY_IPS='127.0.0.1,::1'
```
Cloudflare:
### Cloudflare DNS Backend
```bash
export DDNS_DNS_PROVIDER='cloudflare'
@@ -72,7 +119,9 @@ export DDNS_CLOUDFLARE_ZONE_ID='replace-with-cloudflare-zone-id'
export DDNS_TTL=60
```
RFC 2136:
Use a least-privilege Cloudflare API token limited to the zone RouterPulse should update.
### RFC 2136 DNS Backend
```bash
export DDNS_DNS_PROVIDER='rfc2136'
@@ -83,34 +132,45 @@ export DDNS_RFC2136_KEY_SECRET='replace-with-tsig-secret'
export DDNS_TTL=60
```
`DDNS_DNS_ZONES` is the allowlist of zones this service may publish. A custom domain must pass TXT verification and the requested hostname must be inside one of these zones.
`DDNS_DNS_ZONES` is the allowlist of DNS zones this service may publish. A custom hostname must be inside one of these zones.
## FRITZ!Box Setup
## FRITZ!Box DynDNS Setup
Create a hostname in the web UI. The generated page shows the exact values for the FRITZ!Box DynDNS form:
Create a hostname in the web UI. RouterPulse shows the exact values for:
- `Update-URL`
- `Domainnamen`
- `Benutzername`
- `Kennwort`
The generated update URL uses FRITZ!Box placeholders:
The generated FRITZ!Box URL uses the native placeholders:
```text
/u/<random-update-slug>?myip=<ipaddr>&myipv6=<ip6addr>
https://ddns.example.net/u/<random-update-slug>?myip=<ipaddr>&myipv6=<ip6addr>
```
The legacy compatibility endpoint is also available:
The compatibility endpoint is also available:
```text
/nic/update?hostname=<domain>&myip=<ipaddr>&myipv6=<ip6addr>&username=<username>&password=<pass>
https://ddns.example.net/nic/update?hostname=<domain>&myip=<ipaddr>&myipv6=<ip6addr>&username=<username>&password=<pass>
```
Prefer `/u/<slug>` for managed service use because the router request cannot change the hostname.
## Custom Domain Flow
Custom domains are intentionally simple:
1. Enter the domain.
2. Add the generated TXT record at your DNS provider.
3. Press **I added it, check DNS**.
4. Create router credentials after the TXT record verifies.
RouterPulse stores the domain claim in SQLite and only creates hostnames below verified, publishable zones.
## HTTP API
The API is available under `/api/v1`.
The JSON API is available under `/api/v1`.
| Endpoint | Purpose |
| --- | --- |
@@ -119,7 +179,7 @@ The API is available under `/api/v1`.
| `DELETE /api/v1/management/{management_slug}` | Delete a hostname and its DNS records. |
| `POST /api/v1/domains/challenges` | Create a TXT challenge. Requires login. |
| `POST /api/v1/domains/verify` | Verify a TXT challenge. Requires login. |
| `POST /api/v1/hostnames/custom` | Create a custom hostname below a verified, publishable domain. Requires login. |
| `POST /api/v1/hostnames/custom` | Create a custom hostname below a verified domain. Requires login. |
| `GET /api/v1/updates/{update_slug}` | JSON update endpoint for routers or automation. |
Example:
@@ -141,12 +201,12 @@ docker compose up -d --build
`Caddyfile.example` contains a minimal HTTPS reverse proxy example.
## Operations
## Operations Checklist
- Run behind HTTPS.
- Keep `.env` out of git.
- Use long random values for `DDNS_ADMIN_PASSWORD` and `DDNS_SESSION_SECRET`.
- Use a least-privilege DNS API token limited to the managed zone.
- Use a least-privilege DNS API token or TSIG key.
- Configure `DDNS_TRUSTED_PROXY_IPS` only for reverse proxies that strip user-supplied forwarding headers.
- Back up `/data/ddns.sqlite3`.
- Run one Uvicorn worker with SQLite. The per-host update lock is process-local.
@@ -154,9 +214,13 @@ docker compose up -d --build
SQLite backup example:
```bash
sqlite3 /data/ddns.sqlite3 ".backup '/backups/router-dyndns.sqlite3'"
sqlite3 /data/ddns.sqlite3 ".backup '/backups/routerpulse.sqlite3'"
```
## Project Status
RouterPulse is suitable for self-hosting and private/public beta use when a real DNS backend is configured. For a larger public free DynDNS provider, add external abuse controls, monitoring, alerting, and a tested backup/restore process before launch.
## Development
```bash
@@ -165,6 +229,10 @@ pytest
python -m compileall router_dyndns
```
## SEO Keywords
Self-hosted DynDNS, self hosted DDNS, Dynamic DNS server, FRITZ!Box DynDNS, FRITZ Box DDNS, Cloudflare DDNS, RFC 2136 DDNS, custom DynDNS provider, home lab DDNS, router dynamic DNS, IPv6 DynDNS, open source DynDNS provider, DuckDNS alternative, No-IP alternative, Dynu alternative.
## Security
The repository intentionally contains only placeholders in `.env.example` and documentation. Do not commit a real `.env`, DNS token, TSIG secret, admin password, session secret, database, or router-generated update URL.