DEV Community

Cover image for systemd-resolved broke my TLS cert renewal
Schiff Heimlich
Schiff Heimlich

Posted on

systemd-resolved broke my TLS cert renewal

I ran into something dumb last week. Caddy's certificate renewal kept failing silently, and it took longer than I'd like to admit to figure out the culprit was systemd-resolved.

What happened

Caddy uses ACME challenges to renew certificates. The process involves a DNS query from your server to Let's Encrypt — nothing unusual. Except mine was returning SERVFAIL for the specific TXT record Caddy needed, while every other query worked fine.

The catch: systemd-resolved has a stub resolver behavior where it selectively returns errors for certain record types or domains depending on how your /etc/resolv.conf is configured. In my case, it was filtering outbound queries for _acme-challenge.example.com silently.

How I found it

Running resolvectl query _acme-challenge.example.com showed SERVFAIL, while dig @8.8.8.8 _acme-challenge.example.com TXT returned the correct record immediately. The stub resolver was the problem, not the network or Caddy.

The fix

Temporarily bypass the stub resolver for renewals. Edit /etc/resolv.conf and replace 127.0.0.53 with 8.8.8.8, or point Caddy at an upstream resolver directly:

{
  email "your@example.com"
  acme_ca "https://acme-v02.api.letsencrypt.org/directory"
  resolver "8.8.8.8"
}
Enter fullscreen mode Exit fullscreen mode

The lesson

systemd-resolved is fine until it isn't. When something works manually but fails in automation, the local resolver is worth checking. The kind of thing that only surfaces as a renewal failure when nobody's watching.

Top comments (0)