Build it yourself · Part 1Checked: 30 October 2026
Chapter 3: A small server in the EU
Why
Your devices will send every name they look up to this machine. It should be one you control, in a place whose privacy law you know, and reachable only the way you decide.
Our choice, and the alternatives
We use Hetzner Cloud, in Germany and Finland. Any provider that gives you a small Ubuntu server with a public IPv4 and IPv6 address and a firewall in front of it will do; look for one in the EU if that matters to you.
A resolver for a household is light work. In our setup, the resolver with a few large block lists used about 190 MB of memory. The smallest server is enough.
What you need
An SSH key on your Mac. If you don't have one:
ssh-keygen -t ed25519 -C "dns-server"Press Enter to accept the file name, and choose a passphrase. Your public key is now in
~/.ssh/id_ed25519.pub.- Your home's public IP address, for the firewall. Any "what is my IP" page shows it.
- About 30 minutes (our estimate).
Steps
- Create a separate project in the Hetzner Cloud console, only for this server. It keeps its firewall and keys apart from anything else you run.
- Add your SSH public key to the project (Security → SSH keys). Paste the contents of
~/.ssh/id_ed25519.pub. Create a firewall (Firewalls → Create firewall) with these inbound rules:
Protocol Port From Why TCP 22 your home IP only SSH, for you ICMP – any ping, for checks TCP 80 any certificate renewal (chapter 5) TCP 443 any encrypted DNS for your devices (chapter 4) Leave outbound open: your resolver needs to ask other servers.
Create the server: location in the EU, image Ubuntu (the newest LTS version), type CX23 or the smallest available. Tick your SSH key. Under Firewalls, choose the one from step 3. Under Cloud config, paste the text below, with your own public key in place of
<your-ssh-public-key>.#cloud-config users: - name: dns groups: [sudo] shell: /bin/bash sudo: "ALL=(ALL) NOPASSWD:ALL" lock_passwd: true ssh_authorized_keys: - <your-ssh-public-key> disable_root: true ssh_pwauth: false timezone: Etc/UTC package_update: true package_upgrade: true package_reboot_if_required: true packages: - unattended-upgrades write_files: - path: /etc/ssh/sshd_config.d/10-dns.conf permissions: "0644" content: | PermitRootLogin no PasswordAuthentication no KbdInteractiveAuthentication no AllowUsers dns runcmd: - systemctl restart sshWhat each part does:
users: creates one user,dns, that logs in only with your key (lock_passwd: truemeans it has no password) and may usesudo.disable_root,ssh_pwauth: false: no login as root, no login with a password.package_update,package_upgrade,package_reboot_if_required: the server starts fully updated.unattended-upgrades: installs security updates from Ubuntu by itself from now on.write_files: an SSH setting that refuses root, passwords and every user exceptdns, even if something else changes the defaults later.
- Check that the firewall is attached: open Firewalls → your firewall. It must say "Applied to 1 resource". If it says 0, apply it to the server now (see Pitfalls).
- Switch on delete protection for the server (server → Settings), so a wrong click can't remove it.
- Note the server's IPv4 and IPv6 addresses. You need them in chapters 5 and 8. The console may show the IPv6 as a whole network ending in
::/64; you need the server's own address. After logging in (below), check it withip -6 addr show scope global. At Hetzner it usually ends in::1.
How you know it worked
Wait two or three minutes after creating the server, so the cloud config can finish. Then, from your Mac:
ssh dns@<your-server-ip>
You're logged in. Now check that the doors you closed are really closed:
ssh root@<your-server-ip>
must be refused (Permission denied), and
ssh -o PubkeyAuthentication=no dns@<your-server-ip>
must be refused too: no password login.
From another network (for example your phone's hotspot), ssh dns@<your-server-ip> must not connect at all. That's the firewall.
Pitfalls
- The firewall can stay unattached even if you chose it while creating the server. Our server ran with every port open until we noticed. Always check "Applied to 1 resource".
- Your home IP can change. Many home connections get a new address from time to time. If SSH suddenly stops working, update the firewall rule with your new address before suspecting anything else.
- The cheapest servers are sometimes sold out. In October 2026 the CX line was at times unavailable. Take the next size, or try again later.
Time and cost
- About 30 minutes (our estimate).
- Hetzner CX23: €5.49 a month plus €0.50 for an IPv4 address, without VAT (prices as of October 2026).
Previous: Chapter 2: DNS hosting with DNSSEC · Next: Chapter 4: The resolver
Text: CC BY-SA 4.0 · Commands and configs: CC0