Automatically renew Let's Encrypt Wildcard Certificates

Short troubleshooting for automatic renewal of wildcard certificates for Namecheap domain

Automatically renew Let's Encrypt Wildcard Certificates

Hey again!

This will be a short post just troubleshooting issues from another guide.

There are many different services that automate this process, but unfortunately Namecheap does not support any such automation. As such, some hacky solutions are required.

Before I begin, thanks to the creator of the blog post, Bryan C. Roessler & the creator of acme-dns, Joona Hoikkala!

I won't go into too much details, as everything except a few core pieces are very well explained here!

So, what can possibly go wrong following the guide above?

My issue was that systemd-resolved was already running on port 53.

The following had little effect:

  • Disabling the service - This lead to DNS resolution failing. Not very surprising.
  • Changing systemd-resolved to utilize for DNS lookups. I tried many ways, but primarily through resolv.conf / resolved.conf. I restarted services and tried various way to get DNS to be resolved through ACME-DNS, just for the purpose of automatically renewing the certs.

I started troubleshooting according to the github page.

Running everything manually quickly showed :53 could not be bound to localhost, or (like I did not already know this at that point)

I disabled systemd-resolved and followed these steps to check if there was any issues with the service itself. Everything worked as expected.

Being frustrated and angry at spending 4 hours trying to set up automation that should be simple, I decided to try to just bind the server to my public IP.

Surprisingly though, it worked wonders! Out of the blue, everything worked as expected.

That means, in order for the service to run properly as indicated in the guide linked above, change the acme-dns configuration file from:

listen = ":53"


listen = "<PUBLIC_IP_HERE>:53"

See below for a complete example of the file. As mentioned in the original post, remember to change <> to whatever you domain name is and <your_IP> to whatever public IP your machine has.

# DNS interface - CHANGES BELOW!
listen = "<PUBLIC_IP_HERE>:53"
protocol = "udp"
# domain name to serve the requests off of
domain = "acme.<>"
# zone name server
nsname = "ns1.acme.<>"
# admin email address, where @ is substituted with .
nsadmin = "admin.<>"
# predefined records served in addition to the TXT
records = [
    "acme.<>. A <your_IP>",
    "ns1.acme.<>. A <your_IP>",
    "acme.<>. NS ns1.acme.<>.",
debug = false

engine = "sqlite3"
connection = "/var/lib/acme-dns/acme-dns.db"

api_domain = ""
ip = ""
disable_registration = false
autocert_port = "80"
port = "8081"
tls = "none"
corsorigins = [
use_header = false
header_name = "X-Forwarded-For"

loglevel = "debug"
logtype = "stdout"
logformat = "text"

After which all was good in the world.

As a bonus, I also threw together a very short bash script, to enable the service and open ports only while renewing the certificate. Nothing fancy, but makes it a little safer.

Shell script is as follows, and installed as a cronjob. I am sure there is a better way to implement this, but hey, it works!


checkstatus() {
        if [[ $1 != 0 ]]; then
                echo "$(date) - Command $2 did not complete successfully!" >> /var/log/cert_renew.log

sudo ufw allow 53/udp && sudo ufw reload
checkstatus $? "UFW allow & restart"
sleep 2
sudo systemctl start acme-dns.service
checkstatus $? "Start acme-dns"
sleep 5

/usr/bin/certbot renew --post-hook "systemctl reload nginx" >> /var/log/letsencrypt/renew.log
checkstatus $? "certbot renew - check /var/log/letsencrypt/renew.log"

sleep 5
sudo systemctl stop acme-dns.service
checkstatus $? "Stop acme-dns"
sudo ufw delete allow 53/udp && sudo ufw reload
checkstatus $? "UFW delete & restart"

Also gives a nice little log in case something goes wrong.

Hope this short write-up helped, in case you ran into similar issues.

Until next time,