If you’re like me, you love self-hosting your own projects. But as the number of services you run grows, so does the list of DNS records.

I used to log into my Cloudflare dashboard constantly, adding A record after A record. It felt messy, and I ended up with dozens of entries for my main domain.

I wanted a cleaner solution – a way to isolate my self-hosted projects (like Beszel) from my main site, gain full control over their DNS, and maybe even learn a thing or two about how DNS really works under the hood.

That’s where CoreDNS comes in. It’s a tiny, incredibly fast, and easy-to-configure DNS server that’s perfect for this. With a single configuration file and a lightweight Docker container, you can build your own authoritative DNS server.

In this guide, I'll walk you through the exact steps I took to set up my own authoritative DNS server for a subdomain.

We’re going to build the official source of truth for a dedicated zone, like lab.yourdomain.com. It’s a game-changer for managing a growing collection of self-hosted services.

Any public DNS query for *.lab.yourdomain.com will be correctly delegated and answered by your CoreDNS server, giving you full, instant control over your records.

Don’t worry if this is new – I'll walk you through it, step by step.

📬 NEWSLETTER

If you want more write-ups like this one, subscribe to my newsletter – I send an email when I publish new guides and walkthroughs.

I'm In!

The Big Picture: What We're Building (and What We're Not)

Before we get our hands dirty, let’s zoom out.

Our goal is to build our very own authoritative DNS server. Think of it as creating a definitive, private address book for our own projects.

This guide focuses on using a subdomain (like lab.yourdomain.com) for this project, with our CoreDNS server living at ns1.lab.yourdomain.com.

This approach is known as using an "in-bailiwick" nameserver, a fancy way of saying the nameserver (ns1.lab...) resides within the same subdomain (lab...) that it manages.

It’s a clean and scalable way to structure your DNS. I chose this method to neatly separate my lab services from my main site without needing to buy a new domain, and it's a fantastic, low-cost way to learn if you already own one.

If you decide to create another DNS environment later (like dev.yourdomain.com), you can repeat the pattern without everything getting tangled up.

However, the principles here can also be adapted if you want to use a dedicated root domain (e.g., your-new-cool-project.com).

A quick note on SEO: A common worry is whether using a subdomain will hurt your main site’s search engine ranking. Don’t worry, it won’t. Google and other search engines treat subdomains as separate properties. As long as your projects don’t create security issues, your main domain’s SEO will be completely unaffected.

Authoritative vs. Recursive: A Crucial Distinction

It is vital to understand the type of server we are building.

A DNS server can play two main roles:

  1. Authoritative Server (What we are building): This server holds the "master copy" of records for a specific zone. It only answers questions about domains it controls. When asked about beszel.lab.ivansalloum.com, it confidently says, "I know the answer! It's 192.168.1.100." It is the source of truth.
  2. Recursive Resolver (What we are NOT building): This is the type of DNS server you typically use for your computer or home router (like Cloudflare's 1.1.1.1 or Google's 8.8.8.8). Its job is to go out and find the answer to any query. When you ask it for google.com, it recursively talks to other DNS servers across the internet to find the correct IP address for you.

Our configuration is purely authoritative. It knows nothing about the outside world.

If we allow our server to answer generic queries for anyone on the internet, it would become an "Open Resolver."

Hackers actively hunt for these unsecured servers to launch massive DDoS attacks (called Amplification Attacks) against victims. By keeping our server strictly Authoritative, we keep our infrastructure – and the internet – safer.

Do NOT set this server as the primary DNS on your computer or router.

If you point your laptop's DNS settings only to your new CoreDNS server and then try to visit google.com, this is what happens:

  • Your server looks at its own Corefile.
  • It sees it's only responsible for lab.yourdomain.com.
  • It essentially replies, "I don't know who google.com is, and it's not my job to find out."

The request will fail, and for you, the internet will appear "broken."

Our server is only meant to be found by public resolvers (like Cloudflare or Google) that are looking for answers about our specific subdomain.

Preparing Your Server

Before we dive into CoreDNS specifics, we need to get your server ready.

I’m writing this guide based on my experience with an Ubuntu 24.04 LTS server hosted on a Hetzner VPS, but the steps should be broadly applicable to most Linux distributions.

👉
I run everything on Hetzner because it’s cost-effective and reliable – if you’d like to try them, here’s my referral link.

Set Your Server's Hostname

It’s good practice to give your server a meaningful hostname.

For this setup, it makes sense to use the nameserver's own FQDN (Fully Qualified Domain Name):

sudo hostnamectl set-hostname ns1.lab.ivansalloum.com

This command updates your server's hostname to reflect its role as the nameserver for your lab environment.

Remember to replace ivansalloum.com with your actual domain!

Register Your Nameserver's IP (Glue Records)

It's absolutely crucial to register the IP addresses for your nameserver with your domain registrar or DNS provider. These are often called "glue records" and they tell the internet where ns1.lab.ivansalloum.com actually lives.

Without them, no one will be able to find your custom DNS server.

You'll need to create A and optionally AAAA records for your nameserver's hostname (ns1.lab.ivansalloum.com) at your DNS provider.

Ensure these records are set up to directly point to your server's IP addresses and are not proxied if your provider offers that option (e.g., Cloudflare's orange cloud). DNS traffic for nameservers must hit your server directly.

Also, do not add an A record for just lab at this stage; we are only creating records for ns1.lab itself. The lab subdomain will be delegated later.

Free Up Port 53 (Stop systemd-resolved)

DNS services communicate primarily over port 53.

systemd-resolved (Ubuntu’s local DNS stub) might already be listening on this port, preventing CoreDNS from binding to it. We need to disable its stub listener.

You can check if port 53 is in use with:

sudo lsof -i :53

If you see systemd-resolved listed, follow these steps to disable it.

First, create the directory for systemd-resolved overrides:

sudo mkdir -p /etc/systemd/resolved.conf.d/

Then, set appropriate permissions for the newly created directory:

sudo chmod 755 /etc/systemd/resolved.conf.d/

Next, disable the DNSStubListener by writing the configuration to a file:

printf "[Resolve]\nDNSStubListener=no\n" | sudo tee /etc/systemd/resolved.conf.d/noresolved.conf

Finally, restart systemd-resolved to apply the changes:

sudo systemctl restart systemd-resolved.service

After restarting, sudo lsof -i :53 should no longer show systemd-resolved listening.

Configure Your Firewall (UFW)

For your CoreDNS server to receive queries, you'll need to open the necessary ports in your firewall.

DNS traffic primarily uses UDP port 53, but TCP port 53 is also essential:

sudo ufw allow 53/udp
sudo ufw allow 53/tcp
sudo ufw enable # If UFW is not already enabled
💡
Why both UDP and TCP?
Almost all DNS queries use UDP because it's fast and lightweight. However, if a DNS response is too large, or for critical operations like zone transfers (especially important if you ever add a secondary DNS server), TCP is used.

Install Docker

We'll be running CoreDNS inside a Docker container for ease of deployment and management.

It's crucial to use up-to-date Docker Engine and Docker Compose versions because Ubuntu's default repositories often contain outdated packages.

For optimal performance and the latest features, always rely on Docker's official repositories.

👉
If you need a detailed walkthrough, I've got a dedicated guide: Installing Docker and Docker Compose on Ubuntu (Using Docker’s Official Repository).

Create CoreDNS Directories

We need a dedicated place for CoreDNS's configuration and zone files:

sudo mkdir -p /opt/coredns/{config,zones}
sudo chmod -R 755 /opt/coredns

With these preparations done, your server is ready to host your authoritative CoreDNS instance!

👉
My Full Server Preparation Checklist
We've just done the necessary prep for CoreDNS. If you'd like to see my complete checklist for setting up a new Ubuntu server from scratch for optimal security and performance, I've detailed everything in this guide: Preparing Your Ubuntu Server for First Use

Creating Your First Zone File

Now for the fun part. We need to create a zone file. This is a plain text file that acts as the database for your subdomain.

It contains all the DNS records – like AAAAACNAME, etc. – that tell the world where to direct traffic for hosts within your zone.

First, navigate to the zones directory we created earlier:

cd /opt/coredns/zones

Next, create and open the zone file. It's a common convention to name this file db. followed by your zone name. In my case, it's db.lab.ivansalloum.com:

sudo vim db.lab.ivansalloum.com

Now, paste the following template into the file. This is the heart of your DNS setup:

$ORIGIN lab.ivansalloum.com.
$TTL 3600

@   IN  SOA ns1.lab.ivansalloum.com. admin.ivansalloum.com. (
        2025120601 ; Serial (YYYYMMDDnn) - CHANGE THIS ON EVERY EDIT!
        7200       ; Refresh (2 hours)
        120        ; Retry (2 minutes)
        2419200    ; Expire (28 days)
        3600       ; Negative Cache TTL (1 hour)
)

    IN  NS  ns1.lab.ivansalloum.com.

ns1 IN  A      YOUR_SERVER_IPV4
ns1 IN  AAAA   YOUR_SERVER_IPV6

; --- Your Custom Records Go Here ---
beszel   IN  A  192.168.1.100
hestia   IN  A  192.168.1.1

; --- Optional Wildcard For Instant Testing ---
*        IN  A  192.168.0.10

Once you've pasted the template, here are the three critical edits you must make before saving:

  1. Go through the template and replace
    1. lab.ivansalloum.com.,
    2. ns1.lab.ivansalloum.com.,
    3. and admin.ivansalloum.com.
    4. with your actual subdomain, nameserver FQDN, and admin email.
  2. Replace YOUR_SERVER_IPV4 and YOUR_SERVER_IPV6 with the actual public IP addresses of your CoreDNS server. If you don't use IPv6, simply delete that line entirely.
  3. The line 2025120601 is the zone's version number. It's crucial that you increment this number every time you edit the file. A common format is YYYYMMDDnn, where nn is the revision number for the day. For this first setup, I recommend setting it to today's date in YYYYMMDDnn format. This makes it easy to track your initial zone version.

Save the file and exit the editor (:wq in vim).

A Note on Zone File Naming

You might be wondering why we named our zone file db.lab.ivansalloum.com.

The db. prefix is purely for convention and readability; it's a holdover from BIND, one of the oldest DNS servers, where db stood for "database."

You could name your file records.txt if you wanted, as long as you point to it correctly in your Corefile.

Sticking to convention, however, makes your setup instantly familiar to other sysadmins (and your future self).

What Did We Just Create?

After saving your new zone file, you've essentially created the instruction manual for your lab.ivansalloum.com subdomain. In short, it does two main things:

  • Declares Authority: The SOA and NS records act like official stamps, announcing that your server is in charge of this zone.
  • Builds an Address Book: The A, AAAA, and other records map your service names (like beszel) to their IP addresses.

The most important "address book" entries are the A and AAAA records for ns1 itself. These are called glue records, and they're critical because they tell the internet how to find your nameserver in the first place.

Think of it as setting up the front desk (SOA, NS) and then filling out the directory (A, AAAA, etc.) for your new DNS office. Pretty neat, right?

Configuring CoreDNS with the Corefile

Our zone file is ready, holding all our DNS records.

But how do we tell CoreDNS to actually use it? This is where the Corefile comes in, and honestly, its simplicity is the main reason I fell in love with CoreDNS.

Instead of wrestling with complex, multi-file setups, CoreDNS uses one single, human-readable file to manage everything. It’s where we tell CoreDNS what zones to serve, what plugins to use, and how to behave.

Let's get it set up.

Navigate to the config directory you created earlier:

cd /opt/coredns/config

Now, create and open the Corefile itself:

sudo vim Corefile

Paste this small block of text in. This is all you need to get a basic authoritative server running:

lab.ivansalloum.com:53 {
    log
    errors
    file /zones/db.lab.ivansalloum.com
}

Save and close the file.

This configuration is elegantly powerful. The first line, lab.ivansalloum.com:53, tells CoreDNS to listen for queries for our zone on port 53.

Inside the block, we enable two incredibly useful plugins, log and errors, which are lifesavers for debugging and seeing your server in action.

Finally, the file /zones/db.lab.ivansalloum.com line is the heart of our setup. This plugin points CoreDNS to our zone file, making it the official, authoritative source for all the records within it. The path /zones/db.lab.ivansalloum.com is relative to the inside of our future Docker container.

And that's it. We've defined our records and told CoreDNS how to serve them.

Docker Compose Configuration

Now that we have our zone file and Corefile configured, it's time to bring our CoreDNS server to life. For this, I absolutely love using Docker.

We'll use docker compose to define and run our CoreDNS service. This allows us to set up all the necessary parameters – like ports, volumes, and restart policies – in a single, easy-to-read file.

Let's create our docker-compose.yml file in the main /opt/coredns directory:

sudo vim /opt/coredns/docker-compose.yml

Paste the following content into the file:

services:
  coredns:
    image: coredns/coredns:latest
    container_name: coredns
    restart: unless-stopped
    ports:
      - "53:53/udp"
      - "53:53/tcp"
    volumes:
      - ./config/Corefile:/Corefile:ro
      - ./zones:/zones:ro

Save and close the file.

This docker-compose file defines how our CoreDNS server will run. In short, it:

  • Pulls the official CoreDNS image and names the container coredns.
  • Sets the restart policy to unless-stopped, which is a must-have for any self-hosted service to ensure it automatically starts up after a reboot.
  • Maps the DNS ports (53/udp and 53/tcp) from your host server to the container.
  • Mounts our configuration files (the Corefile and our zones directory) into the container in read-only mode, so CoreDNS can use them.

With this docker-compose.yml in place, we're just one command away from launching our very own authoritative DNS server!

Launching CoreDNS and Local Testing

We've prepared our server, crafted our zone file, and configured CoreDNS. Now, let's fire up our container and make sure everything is working as expected.

First, navigate to the main /opt/coredns directory where your docker-compose.yml file is located:

cd /opt/coredns

Now, launch CoreDNS with Docker Compose in detached mode (-d), meaning it will run in the background:

sudo docker compose up -d

You should see output indicating that the coredns container has been created and started.

With our CoreDNS server now running, let's immediately verify it's answering queries locally before we go live. This local verification step is crucial as it confirms our setup works before we expose it to the internet.

We'll use the dig command-line tool to do this. The trick is to query your own server's IP address directly, bypassing any other DNS resolvers. When we query our server directly, we're bypassing public resolvers to test our server in isolation.

💡
What's a DNS Resolver?
A DNS resolver is a server (like 1.1.1.1 or 8.8.8.8) that clients (your computer) query to find the IP address of a domain name. It handles the entire process of finding the authoritative nameserver and returning the answer to you.

Remember to replace YOUR_SERVER_IPV4 with your server's actual public IPv4 address.

First, let's confirm our server is authoritative for the zone by querying the SOA record:

dig @YOUR_SERVER_IPV4 lab.ivansalloum.com SOA

You should see your SOA record returned, matching the details you put in db.lab.ivansalloum.com.

Next, let's query one of the specific test records we added, like beszel:

dig @YOUR_SERVER_IPV4 beszel.lab.ivansalloum.com

This should return the A record for beszel (e.g., 192.168.1.100).

Finally, if you included the optional wildcard record, test it:

dig @YOUR_SERVER_IPV4 anything.lab.ivansalloum.com

This should resolve to the IP address defined in your wildcard entry.

If all these dig commands return the expected answers, congratulations! Your authoritative DNS server is fully functional and ready to serve requests for your subdomain.

The next step is to tell the rest of the internet to start asking your server for these records.

We're almost there!

Delegating Your Subdomain

Our CoreDNS server is humming along locally.

The final, exhilarating step is to tell the world – specifically, through your domain registrar or DNS provider (in my case, Cloudflare) – that for your chosen subdomain, your CoreDNS server is now the one in charge. This is called delegation.

It's a powerful moment, making your self-hosted DNS server officially responsible for lab.ivansalloum.com (or whatever subdomain you chose).

Head over to your domain registrar or DNS provider and add a new NS (Nameserver) record for your subdomain with the following details::

  • Type: NS
  • Name: lab (or your chosen subdomain part, e.g., lab.ivansalloum.com)
  • Value: ns1.lab.ivansalloum.com. (your nameserver's FQDN, with a trailing dot!)
  • TTL: Auto is usually fine.

Before you save this record, there are a few crucial sanity checks to perform:

  • Don't Forget Your Glue Records: This entire process will only work if you've already created the A and AAAA records for ns1.lab.ivansalloum.com as we did in the "Preparing Your Server" section. These "glue records" are what allow the internet to find your nameserver in the first place.
  • Ensure "DNS Only" Mode: If your provider (like Cloudflare) offers a proxy service, make sure it is disabled for this NS record (e.g., a "grey cloud" in Cloudflare). The internet needs to be able to reach your nameserver directly.
  • Avoid Conflicting Records: Make sure you do NOT have an A record for lab itself. A subdomain can either be delegated (with an NS record) or point to an IP (with an A record), but it can't do both.

Once you've confirmed these points and saved your new NS record, you're ready for the final validation.

Public Validation: Confirming Your Delegation

The CoreDNS server is running, the zone file is configured, and your DNS provider has been updated to delegate your subdomain.

Now comes the exciting part: confirming that the entire internet sees your CoreDNS server as the authoritative source for your subdomain.

While DNS changes can sometimes take a while to propagate, subdomain delegation is usually quite fast – I've often seen it go live within 1 to 10 minutes.

Once you've given it a moment, you can verify your setup from any machine other than your CoreDNS server. This ensures you're querying external DNS resolvers, just like the rest of the world. We'll also use dig for this.

First, let's ask a public resolver (like 1.1.1.1) to confirm that our new nameserver is correctly set up for the subdomain:

dig lab.ivansalloum.com NS @1.1.1.1

You should see your nameserver (ns1.lab.ivansalloum.com.) in the answer section, which confirms the delegation is visible.

Next, let's test that your CoreDNS server is serving the actual records. Try querying one of the custom records you created in your zone file:

dig beszel.lab.ivansalloum.com @1.1.1.1

This should return the A record for beszel that you defined earlier.

If both of these commands work, your authoritative DNS server is officially live and serving records to the internet!

Managing Your DNS Records

Now that your authoritative DNS server is live, the best part is how easy it is to manage your records. The process is simple and gives you full control.

Here’s the step-by-step workflow to update or add new records.

Step 1: Edit Your Zone File

First, open your zone file:

sudo vim /opt/coredns/zones/db.lab.ivansalloum.com

Now, you can either change an existing record or add new ones.

CoreDNS supports all standard DNS record types, including A, AAAA, CNAME, TXT, and more.

For example, to add new A, CNAME, and TXT records, you would add lines similar to these:

; --- Your Custom Records Go Here ---
beszel     IN  A      192.168.1.100
hestia     IN  A      192.168.1.1
wordpress      IN  A      192.168.1.105   ; A record for our new WordPress instance
www.beszel IN  CNAME  beszel.lab.ivansalloum.com. ; CNAME for www.beszel pointing to beszel
@          IN  TXT    "v=spf1 include:_spf.example.com ~all" ; TXT record for SPF

Remember to replace the example values with your own service names and data.

Step 2: Increment the Serial Number (Crucial!)

This is a critical step, primarily for secondary DNS servers (if you ever add them) to know your zone file has changed and needs updating.

The only strict rule is that the serial number must be a single number that strictly increases every time you make a change. However, using the YYYYMMDDnn format (Year, Month, Day, and a two-digit revision for changes on that day) is a widely-adopted best practice for human readability and sanity.

Here's how to manage it:

  • First change of the day: Update the date part to today's date and set the two-digit counter to 01. For example, if your old serial was 2025120202 and today is December 7th, 2025, your new serial would be 2025120701.
  • Subsequent changes on the same day: Only increment the last two digits. For example, a second change on December 7th would make it 2025120702.

Always remember to increment this number whenever you modify your zone file.

Step 3: Restart the CoreDNS Container

After saving and closing the file, you need to restart the CoreDNS container so it reloads your updated zone file.

Navigate to the directory where your docker-compose.yml file is located:

cd /opt/coredns

Then, simply run the restart command:

sudo docker compose restart

This command will gracefully restart the coredns service. It's very fast, usually taking just a second or two.

Once it restarts, CoreDNS will have loaded your new records.

Step 4: Verify Your Changes

Finally, to be sure everything worked, test your new record with dig from your local machine:

dig wordpress.lab.ivansalloum.com @1.1.1.1

You should see the new IP address (192.168.1.105) reflected in the answer.

That's it! You can repeat this process any time you need to add, update, or remove a DNS record.

Automating Zone Reloads (The Pro Way)

Manually restarting the CoreDNS container after every zone file change is simple and effective, but there’s an even better, more professional way: using the reload plugin.

This plugin watches your zone file for changes and, if it detects any, gracefully reloads the zone in the background with zero downtime. You never have to restart the container for simple record updates again!

Let's enable it.

Edit your Corefile one more time:

sudo vim /opt/coredns/config/Corefile

Add the reload plugin to the top of your server block. It should look like this:

lab.ivansalloum.com:53 {
    reload
    log
    errors
    file /zones/db.lab.ivansalloum.com
}

Save the file, and then restart the container one last time to activate the reload plugin itself:

cd /opt/coredns
sudo docker compose restart

From now on, you can simply edit your db.lab.ivansalloum.com file, and CoreDNS will automatically apply the changes within about 30 seconds (the default check interval).

This small change makes managing your DNS records an even more seamless experience.

Don't forget to increase your serial number!

The reload plugin is smart. It only reloads the zone if it detects that the serial number has increased.

If you forget to increment the serial number, the plugin will assume nothing has changed and won't apply your updates.

So, the workflow remains:

  1. Edit your records,
  2. increment the serial,
  3. and save the file.

The restart step is now automated for you.

Seeing the Difference: dig in Action

We've talked about the difference between an authoritative server and a recursive resolver, but let's see it live using dig.

This is the best way to truly understand the roles they play.

Query 1: A Standard Recursive Query

When you run dig for a public domain, your computer uses the default DNS resolver provided by your router or ISP:

dig ivansalloum.com

This is a recursive resolver.

Notice the flags in the header of the response: qr rd ra.

  • qr = Query Response
  • rd = Recursion Desired (You asked the resolver to find the answer for you)
  • ra = Recursion Available (The resolver is confirming it can do this)

The resolver went out and found the answer for you.

Query 2: Specifying a Public Resolver

We can get the same kind of recursive answer by specifying a different public resolver, like Cloudflare's 1.1.1.1:

dig ivansalloum.com @1.1.1.1

Again, you'll see the ra flag.

1.1.1.1 is a recursive resolver, and it happily found the answer.

Query 3: Asking Our Authoritative Server for a Public Domain (The "Broken" Test)

Now, let's ask our new CoreDNS server to find ivansalloum.com:

dig ivansalloum.com @YOUR_SERVER_IPV4

The result is completely different.

The status is REFUSED, and you might see a warning like recursion requested but not available.

Our server correctly refused the query because it's not a public resolver and has no idea who ivansalloum.com is.

Query 4: Asking Our Authoritative Server for a Zone It Controls

This is where our server shines.

Let's ask it for a record from the zone it's responsible for:

dig beszel.lab.ivansalloum.com @YOUR_SERVER_IPV4

This query succeeds beautifully! Now, look very closely at the flags in the header: qr aa rd.

That aa flag is the key. It stands for Authoritative Answer. This is the DNS system's way of confirming that the answer came directly from the server that is the ultimate source of truth for that domain.

You will not see this aa flag when you query for beszel.lab.ivansalloum.com using a public resolver like 1.1.1.1, because 1.1.1.1 is just relaying the answer it found from your server.

This aa flag is your proof that you have successfully built and queried a true authoritative DNS server.

Backup and Recovery with Hetzner Snapshots

Even with a stable DNS server, having a disaster recovery plan is essential for peace of mind.

For my setup on Hetzner, their snapshot feature combined with IP protection has proven to be a robust and reliable solution for disaster recovery.

The goal was to simulate a total server failure and see if I could bring my DNS server back to life from a backup.

Here's the strategy I used, combining two powerful Hetzner features.

The Strategy: Protected IPs + Snapshots

  1. Enable IP Protection: Before anything else, I went to my Hetzner Cloud Console and enabled "IP protection" on my server's public IPv4 and IPv6 addresses. This is a critical feature that prevents your IPs from being lost if the server is deleted. It turns them into floating IPs that you can re-assign to a new server.
  2. Take a Snapshot: With my CoreDNS server running and serving records for a test service (beszel.lab.ivansalloum.com), I took a manual snapshot of the server.

My Disaster Recovery Test

With the snapshot created and the IPs protected, it was time for the real test: I deleted the server entirely.

An interesting thing happened right after. On my home WiFi, beszel.lab.ivansalloum.com still worked for a few minutes. This is DNS caching in action! My local router or ISP's resolver had cached the record and didn't need to ask for it again.

However, when I switched to my phone's 5G network (a "fresh" resolver), the domain failed to resolve immediately. This perfectly demonstrates why DNS resilience is so important – cached records will eventually expire, and a single server outage will be felt.

Next, I provisioned a new server from the snapshot I had created. During the creation process, I re-attached my protected IPv4 and IPv6 addresses to the new instance.

The result? Within moments of the new server booting up, CoreDNS was back online.

My beszel.lab.ivansalloum.com domain started resolving correctly on all networks.

It just worked.

Automated Backups & Protection

While manual snapshots are great for point-in-time recovery, Hetzner offers additional layers of protection:

  • Automated Backups: For more frequent and automated restore points, consider enabling Hetzner's backup add-on. For a small additional cost (typically 20% of the server cost), this service provides daily backups that are kept for a week. A key advantage is that you can convert any automated backup into a manual snapshot before performing a restore or server deletion.
  • Deletion Protection: To prevent accidental deletion of your server (and thus, your DNS service), always enable deletion protection in the Hetzner Cloud Console. It's a simple toggle that can save you a lot of headache.

This comprehensive approach ensures you're well-covered for any eventuality.

Conclusion and Final Thoughts

Congratulations! You've successfully deployed your very own authoritative DNS server with CoreDNS, giving you unparalleled control over your subdomains.

This setup empowers you to:

  • Isolate your self-hosted projects: Keep your main domain's DNS clean and separate.
  • Enjoy instant control: Add, update, or remove records in seconds, without waiting for third-party providers.
  • Deepen your DNS understanding: You've walked through the core concepts of DNS delegation, zone files, and record management.

This journey into self-hosting your DNS is a fantastic step towards greater independence and control in your personal infrastructure.

Keep experimenting, keep learning, and enjoy the power you now have at your fingertips.


If you run into any issues or need further help, feel free to revisit this guide or reach out for assistance.

If you found this guide helpful, consider subscribing to my newsletter (beneath the “Read next” section) for more tips and walkthroughs on self-hosting, security, and everything in between!

And if have thoughts and feedback, drop them in the discussion section; I always enjoy seeing how others build on this.