Documentation
How to host a root domain with primary at home and secondary on a VPS
This walkthrough sets up two Everlock instances to serve one root domain:
- Everlock A runs at home behind your Fritzbox on a dynamic public IP. You edit the zone here.
- Everlock B runs on a small VPS with a static public IPv4. It serves every real query from the internet.
The trick: only the VPS's IP is ever registered at the registrar. The home IP stays private. When the home line reconnects, the secondary learns the new endpoint from the next NOTIFY's source IP and re-fetches the zone — no glue updates at the registrar, ever.
This is the recommended residential-friendly setup. If you have a static IP everywhere, the single-instance static guide is simpler. If you want to host only from home and accept the operational friction, see the NAT + GoDaddy guide.
What you need
- A registered domain (
jens.devin this walkthrough). - A box at home that can run Everlock and accept inbound TCP 53 from the VPS. Inbound UDP 53 is not required — the secondary doesn't query for normal records, only does AXFR over TCP.
- A small VPS with a public static IPv4 (€3/month tier is plenty). Both UDP and TCP 53 open inbound.
- The home line needs a port-forward for inbound TCP 53 only.
1. Open the right ports
On the VPS (Everlock B)
sudo ufw allow 53/udp sudo ufw allow 53/tcp
Plus the same in the cloud firewall, if your provider has one. UDP/53 is for normal resolver queries; TCP/53 is for AXFR + large responses + the cloud's resolver fallback.
On the home Fritzbox (Everlock A)
Forward only TCP port 53 inbound to the Everlock host:
Internet → Permit Access → Port Sharing.- Protocol TCP, external
53→ internal53on the Everlock host's LAN IP. - Save.
Don't forward UDP 53. The home node only needs to send NOTIFY (outbound UDP, no port-forward) and answer AXFR (inbound TCP). The world doesn't see the home box at all.
2. Start both Everlock instances
Home (Everlock A)
everlock serve \ --frontend-ssh \ --backend-admin-ssh \ --frontend-dns \ --frontend-dns-listen-udp 0.0.0.0:53,[::]:53 \ --frontend-dns-listen-tcp 0.0.0.0:53,[::]:53 \ --backend-dns-dns \ --admin-user admin --admin-password change-me
VPS (Everlock B)
Same command, same flags. Different data directory of course.
Both bind port 53 — needs root or CAP_NET_BIND_SERVICE:
sudo setcap CAP_NET_BIND_SERVICE=+eip ./everlock
3. Generate a shared TSIG key
The two Everlocks authenticate every NOTIFY and AXFR with a shared HMAC-SHA256 secret. Generate it once on either side; the key value goes into both zone TOMLs verbatim.
SSH into either Everlock and run:
ssh -p 2222 admin@<host>
/dns peer keygen
You get something like:
Generated TSIG key (hmac-sha256, 32 bytes): rJqM2X7nP9eF4kVZ1aL8sQwY3uH6tD0bC5gN+iWvxk0=
Save that string — you'll paste it into both zone files in the next steps. Anyone with that key can forge NOTIFY/AXFR for the zone, so don't paste it into chat logs.
4. Configure the home Everlock (primary)
On the home box, create the zone first:
/users grant admin ssh/dns/* owner /dns zones create jens.dev
Then declare the VPS as a downstream peer. Newly-created zones are
already mode = "primary", so this is a single command — paste the
key from the previous step as secret=:
/dns peer create jens.dev name=vps address=203.0.113.42 \ secret=rJqM2X7nP9eF4kVZ1aL8sQwY3uH6tD0bC5gN+iWvxk0=
address is the VPS's public IPv4; algorithm defaults to
hmac-sha256. The command writes the [[peer]] block into
data/everlock-dns/jens.dev.toml and reloads for you — you never
touch the file by hand. (You can still edit the TOML directly if you
prefer; both paths end in the same place.) Managing peers requires
the owner role on the zone, which /dns zones create granted you
above.
If one secondary answers from several nameserver IPs under a single shared key — common with hosted secondary providers — pass them all, comma-separated, and every one receives the NOTIFY:
/dns peer create jens.dev name=vps \ address=203.0.113.42,203.0.113.43,203.0.113.44 \ secret=rJqM2X7nP9eF4kVZ1aL8sQwY3uH6tD0bC5gN+iWvxk0=
Also set the home Everlock's address mode in data/everlock-system/config/dns.toml
so it knows what to announce as its own ns1/ns2 glue (this is
internal — only the secondary will ever see these records, not the
registrar):
enabled = true listen_udp = "0.0.0.0:53,[::]:53" listen_tcp = "0.0.0.0:53,[::]:53" address_mode = "discover"
Reload:
/dns reload /dns peer list jens.dev
/dns peer list jens.dev should show the vps peer, its
hmac-sha256 algorithm, and its NOTIFY address(es).
5. Configure the VPS Everlock (secondary)
On the VPS, create the zone in secondary mode and point it at the home node as its upstream master. Two commands:
/users grant admin ssh/dns/* owner /dns zones create jens.dev mode=secondary /dns peer create jens.dev name=vps address=203.0.113.99 \ secret=rJqM2X7nP9eF4kVZ1aL8sQwY3uH6tD0bC5gN+iWvxk0=
mode=secondarymakes this a mirror: its records are owned by the primary and arrive via AXFR, so record edits are rejected here.name=vpsmust match the[[peer]]name on the home side.address=203.0.113.99is the home line's current public IPv4. It's a one-time bootstrap — the secondary learns the home node's actual current IP from the source of the first signed NOTIFY that arrives, and keeps it up to date in memory from then on. Any reachable value works to start.
On a secondary, peer create sets the single [primary_peer] block;
it errors if one already exists — use /dns peer set to replace it.
(Both commands just write data/everlock-dns/jens.dev.toml and
reload; you can still edit that file by hand if you prefer.)
Then in data/everlock-system/config/dns.toml:
enabled = true listen_udp = "0.0.0.0:53,[::]:53" listen_tcp = "0.0.0.0:53,[::]:53" address_mode = "static" default_ipv4 = "203.0.113.42" default_ipv6 = "2001:db8::42" # if you have v6
Reload:
/dns reload /dns records list jens.dev
Right after a /dns reload, before any AXFR has happened, the
secondary serves SERVFAIL for the zone (it has the zone config but
no records yet). The next step triggers the AXFR.
6. Trigger the first replication
The /dns peer create in step 4 already fired the first NOTIFY, so
the VPS may have transferred the zone the moment you configured it.
To trigger replication again on demand, change something — the SOA
serial only advances when the zone's effective content actually
changes, so a bare /dns reload with no edits will not NOTIFY
(it reuses the existing serial). Make a real change, e.g.:
/dns records create jens.dev name=_test type=TXT value=hello ttl=300
Watch the logs on both sides:
- Home:
NOTIFY sent (zone=jens.dev, peer=vps, serial=…) - VPS:
DNS NOTIFY for jens.dev from <home-ip>:…followed bymaster endpoint for zone jens.dev set to <home-ip>:53and thenAXFR succeeded for jens.dev (serial …).
Verify the VPS now serves the zone:
dig @203.0.113.42 jens.dev SOA dig @203.0.113.42 jens.dev NS
You should see authoritative answers (AA flag).
On the VPS:
/dns records list jens.dev
Every row should be tagged replicated.
7. Register the delegation at the registrar
This is the same step as in the single-static-IP guide, but with one crucial difference: the glue points only at the VPS's IPs, never at your home IP.
In the registrar panel for jens.dev:
a) Set the nameservers
Switch the delegation to:
ns1.jens.devns2.jens.dev
b) Register glue records (registry-level A/AAAA)
| Host | IPv4 | IPv6 (optional) |
|---|---|---|
ns1 | 203.0.113.42 | 2001:db8::42 |
ns2 | 203.0.113.42 | 2001:db8::42 |
Both NS names resolve to the VPS. (If you eventually run a second
VPS for redundancy, point ns2 glue at its IP. Phase 1 doesn't
require this.)
Save. The home IP is never published anywhere public.
8. Verify the public path
dig @8.8.8.8 jens.dev SOA dig @1.1.1.1 jens.dev NS dig +trace jens.dev
+trace should walk from the root → .dev → ns1.jens.dev
(your VPS) and return authoritative answers from there.
9. Add records — they auto-replicate
From now on, edit records on the home Everlock:
/users grant admin ssh/dns/jens.dev writer /dns records create jens.dev name=@ type=A value=203.0.113.42 ttl=300 /dns records create jens.dev name=www type=CNAME value=jens.dev ttl=300
Each /dns records create reloads the zone, bumps SOA, and NOTIFYs
the VPS. The VPS pulls AXFR and starts answering with the new
records within seconds.
Verify against the VPS:
dig @203.0.113.42 www.jens.dev CNAME
You'll never need to edit anything on the VPS again — it's a pure mirror.
10. What happens when the home IP changes
This is what the whole topology buys you. Walk through the chain on the next forced reconnect:
- Home line reconnects with a new public IPv4.
- Within minutes, Everlock A's
discoverloop notices and bumps its internal records' SOA. - NOTIFY fires from the new source IP, signed with the shared TSIG key.
- The VPS verifies the TSIG, stores the new source IP as the master endpoint, and pulls AXFR back to it.
- The VPS serves the latest zone unchanged. The internet sees no blip.
You don't touch the registrar. You don't touch the VPS. You don't touch glue records. The home IP is private knowledge between you and the VPS.
If the home Everlock is offline (you're rebooting, the DSL line is down), the VPS keeps serving the last zone it AXFR'd. Once the home node comes back, the next NOTIFY re-syncs everything.
Troubleshooting
"AXFR failed: TSIG verification failed"
The secret strings don't match. Re-copy the key from
/dns peer keygen into both zone TOMLs exactly — base64 padding
included.
"NOTIFY failed after retries"
The home → VPS UDP path isn't working. Check that the VPS firewall allows inbound UDP 53 from anywhere. NOTIFYs come from arbitrary source ports.
"no master endpoint known for zone jens.dev"
The secondary hasn't received a NOTIFY since startup. Either make a
record change on the home side to emit one (a bare /dns reload
won't — the serial only moves on a real change), or wait for the
hourly heartbeat to kick the AXFR client (which will fall back to the
bootstrap address in [primary_peer]).
"AXFR rejected: TSIG verification failed for vps"
The TSIG key in the home zone TOML ([[peer]] secret) and the VPS
zone TOML ([primary_peer] secret) differ. They must be identical.
/dns reload on the VPS shows the zone but dig returns SERVFAIL
The secondary has the zone config but no AXFR has completed. Make a record change on the home side to emit a NOTIFY; if nothing happens, check the home Everlock's log for the NOTIFY attempt, and check that the home firewall lets TCP 53 inbound from the VPS for the AXFR.
Phase 1 limitations to know about
- Single-message AXFR. Zones must fit in ~64KB on the wire (~500 typical records). Plenty for a personal domain.
- No incremental transfers. Everlock keeps no IXFR journal, so
every transfer is the full zone. An incoming
IXFRrequest is answered — with a full transfer (RFC 1995 §4 fallback) — so secondaries that prefer IXFR (most hosted providers) work fine; they just receive the whole zone each time. Fine for small zones. - Stable SOA serial. The serial only advances when the zone's
effective content changes, so redundant reloads don't NOTIFY-storm
your secondaries. A consequence: a bare
/dns reloadis not a way to force a re-transfer — make a real change instead. - Replication is fully CLI-driven.
/dns zones create <zone> [mode=primary|secondary]picks the zone mode (default primary), and/dns peer keygen|list|create|set|deletemanage the peers. Every one of these writes the zone TOML and reloads for you; editing the TOML by hand and running/dns reloadremains equivalent if you prefer it. The only replication knob still file-only is the runtimeaddress_mode/listen config ineverlock-system/config/dns.toml.
See the internal design doc for the full list and the server-replication design for the deferred-work table.
Read next
- DNS backend reference
- Address resolution modes
- Standalone NAT-only setup — for comparison, the "no VPS, manual glue updates" alternative.