Self-hosted Dynamic DNS service built for developers. Multi-user, API-driven, with custom domain support and a clean dashboard.
We offer a running instance at YourDDNS.com, free to use!
YourDDNS is free and open-source. If you find it useful, please consider sponsoring to help cover server costs and ongoing development.
Even a small contribution helps keep the service running for everyone.
- Multi-user with email/password, OTP (passwordless), and Google OAuth login
- Email verification and password recovery via Resend
- Account tiers with configurable limits (records, TTL, lookups, updates)
- Multiple DNS zones — full domain delegation or subdomain-only
- Bring your own domain — validate NS delegation and manage static + DDNS records
- Per-record API tokens (PAT) — shown once, regeneratable
- DNS resolution logging with per-record hit tracking
- Reports tab with date-range charts per subdomain
- IPv4 and IPv6 support (A and AAAA records, independent update endpoints)
- Admin portal: user management, domain management, blocked IPs, settings, stats
- Login-as-user impersonation for admins
- Donation prompts with PayPal integration (optional)
- Docker + Caddy (automatic HTTPS via Let's Encrypt) ready
This app runs its own authoritative DNS server. You configure NS records at your registrar to delegate zones to this server.
| Goal | Configure at registrar |
|---|---|
DDNS at *.d.yourddns.com |
d.yourddns.com NS ns1.yourddns.com |
DDNS at *.yourddns.com (full zone) |
yourddns.com NS ns1.yourddns.com |
| User's own domain | ddns.example.com NS ns1.yourddns.com |
For full-zone delegation, Admin → Domains lets you manage static DNS records (A, MX, CNAME, TXT) alongside DDNS records.
-
DNSSEC not supported — the server does not sign responses and does not publish DNSKEY, RRSIG, or NSEC records. Validating resolvers will treat all responses as unsigned. DNSSEC requires EDNS(0) (which is implemented), so adding signing support is the primary remaining gap.
-
ANY query returns all records — the server answers
QTYPE=ANYwith every record at the queried name. RFC 8482 recommends responding with a minimal answer (e.g. a single HINFO record) to discourage amplification. This does not affect normal A/AAAA lookups.
# 1. Clone the repo
git clone https://github.com/ryangriggs/yourddns
cd yourddns
# 2. Configure environment
cp .env.example .env
# Edit .env — set SESSION_SECRET, PAT_HMAC_SECRET, RESEND_API_KEY, ADMIN_EMAIL, ADMIN_PASSWORD, SITE_DOMAIN
# 3. Generate secrets
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Run twice — use outputs for SESSION_SECRET and PAT_HMAC_SECRET
# 4. Disable systemd-resolved if it holds port 53 (common on Ubuntu)
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
echo "nameserver 1.1.1.1" | sudo tee /etc/resolv.conf
# 5. Set up iptables forwarding for DNS (53 → 5300)
# Scoped to eth0 — prevents Docker's internal DNS from being redirected
sudo iptables -t nat -I PREROUTING -i eth0 -p udp --dport 53 -j REDIRECT --to-port 5300
sudo iptables -t nat -I PREROUTING -i eth0 -p tcp --dport 53 -j REDIRECT --to-port 5300
sudo apt-get install -y iptables-persistent && sudo netfilter-persistent save
# 6. Start
docker compose up -dCaddy handles HTTPS automatically via Let's Encrypt on first startup — no manual certificate steps required.
| Variable | Required | Description |
|---|---|---|
SESSION_SECRET |
Yes | 32+ char random string for session encryption |
PAT_HMAC_SECRET |
Yes | Random string for PAT hashing |
ADMIN_EMAIL |
Yes | Bootstrap admin email (first startup only) |
ADMIN_PASSWORD |
Yes | Bootstrap admin password |
SITE_DOMAIN |
Yes | Primary domain (e.g. yourddns.com) |
CADDY_EMAIL |
Yes | Email for Let's Encrypt certificate notifications |
RESEND_API_KEY |
No | Resend API key for transactional emails |
EMAIL_FROM |
No | From address for emails |
SITE_NAME |
No | Display name (also configurable in Admin → Settings) |
SITE_URL |
No | Full URL including https:// |
GOOGLE_CLIENT_ID |
No | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
No | Google OAuth client secret |
DB_PATH |
No | SQLite path (default: ./data/yourddns.db) |
STRIPE_SECRET_KEY |
No | Stripe secret key (optional billing) |
Clients update their IP with a single GET request:
GET https://yourddns.com/api/update?key=YOUR_PAT&subdomain=home.d.yourddns.com
Optional parameters:
| Parameter | Description |
|---|---|
ip |
Set IPv4 explicitly (default: auto-detect from connecting IP) |
ip6 |
Set IPv6 explicitly |
subdomain |
Fully-qualified hostname to update |
name |
Device/computer name logged with each update — visible in the dashboard update history (max 64 chars) |
Responses: good 1.2.3.4 (updated), nochg 1.2.3.4 (no change), badauth, badip, abuse, disabled
# Update IPv4 and IPv6 every 5 minutes, tagging with the machine hostname
*/5 * * * * curl -s -4 "https://yourddns.com/api/update?key=YOUR_PAT&subdomain=home.d.yourddns.com&name=$(hostname)" > /dev/null
*/5 * * * * curl -s -6 "https://yourddns.com/api/update?key=YOUR_PAT&subdomain=home.d.yourddns.com&name=$(hostname)" > /dev/null| Tier | Records | Min TTL | Resolutions/hr | Updates/hr |
|---|---|---|---|---|
| Free | 3 | 300s | 1,000 | 10 |
| Starter | 10 | 120s | 5,000 | 30 |
| Pro | 50 | 60s | 20,000 | 60 |
All values are configurable in Admin → Settings. Subscriptions can be disabled entirely (everyone gets free-tier limits) — useful for running a community or personal instance.
The DNS server binds to port 5300 (no root required), exposed on host port 5300. Public port 53 is forwarded to 5300 via iptables.
# Scoped to eth0 — do NOT omit -i eth0 or Docker containers will be unable
# to resolve external DNS during builds and at runtime
sudo iptables -t nat -I PREROUTING -i eth0 -p udp --dport 53 -j REDIRECT --to-port 5300
sudo iptables -t nat -I PREROUTING -i eth0 -p tcp --dport 53 -j REDIRECT --to-port 5300
sudo apt-get install -y iptables-persistent && sudo netfilter-persistent saveVerify DNS is working:
dig @<your-server-ip> yourdomain.d.yourddns.comAll site-level settings (name, domain, support email, PayPal donation URL, etc.) are configurable in Admin → Settings without redeployment.