Acme-dns on CentOS 7

Setting up acme-dns on CentOS 7 and configuring a client

In this post an acme-dns server will be set up and a client will acquire a Let’s Encrypt certificate using the DNS-01 challenge.

Acme-dns provides a simple API exclusively for TXT record updates and should be used with ACME magic “_acme-challenge” - subdomain CNAME records. This way, in the unfortunate exposure of API keys, the effects are limited to the subdomain TXT record in question.

Network topology

In this post the following is assumed:

The future acme-dns server has an IP address of The following DNS records have been created: A NS

If you manage your own DNS or your provider supports it, you can just use NS Cloudflare does not support records for a host if a different nameserver was set, so I will use the subdomain a. for the acme-dns-managed DNS entries.


To install acme-dns we need git, gcc and go.

sudo yum install git gcc -y
# install go:
sudo tar -C /usr/local -xzf go1.11.4.linux-amd64.tar.gz
sudo ln -s /usr/local/go/bin/* /usr/local/bin

Install acme-dns as a service

The following will build and install the acme-dns binary.

go get
sudo mv ~/go/bin/acme-dns /usr/local/bin/acme-dns

Now we need to add a user for the acme-dns service and set up the service itself

sudo adduser --system --home /var/lib/acme-dns acme-dns
sudo mkdir /var/lib/acme-dns
sudo chown acme-dns: /var/lib/acme-dns
sudo wget -O /etc/systemd/system/acme-dns.service
sudo systemctl daemon-reload

As I was getting errors trying to set the capabilities (allow binding to a port <1000) in the service configuration, I removed that part from the configuration and gave the executable the required permissions directly:

sudo sed -i 's/AmbientCapabilities/#AmbientCapabilities/g' /etc/systemd/system/acme-dns.service
sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/acme-dns
sudo systemctl daemon-reload

Configure acme-dns

We can use the example configuration file with our own dns-name and ip address.

sudo mkdir /etc/acme-dns
sudo wget -O /etc/acme-dns/config.cfg
sudo sed -i 's/^listen = .*/listen = ":53"/g' /etc/acme-dns/config.cfg
sudo sed -i 's/' /etc/acme-dns/config.cfg
sudo sed -i 's/' /etc/acme-dns/config.cfg
sudo sed -i 's/^connection = .*/connection = "\/var\/lib\/acme-dns\/acme-dns.db"/g' /etc/acme-dns/config.cfg

Acme-dns supports acquiring it’s own SSL certificate via Let’s Encrypt and serving it’s API on https.

sudo sed -i 's/^tls = .*/tls = "letsencrypt"/g' /etc/acme-dns/config.cfg
sudo sed -i 's/^port = .*/port = "443"/g' /etc/acme-dns/config.cfg
sudo sed -i 's/^api_domain = .*/api_domain = ""/g' /etc/acme-dns/config.cfg
sudo sed -i 's/^acme_cache_dir = .*/acme_cache_dir = "/var/lib/acme-dns/cert"/g' /etc/acme-dns/config.cfg

Starting the service

sudo systemctl enable acme-dns.service
sudo systemctl start acme-dns.service


Now we can switch to the client that is supposed to get it’s own certificate issued. First we need to register a new subdomain on the acme-dns server. The response will contain the details needed for the next steps.

curl -X POST
		"username":		"eabcdb41-d89f-4580-826f-3e62e9755ef2",
		"password":		"pbAXVjlIOE01xbut7YnAbkhMQIkcwoHO0ek2j4Q0",
		"fulldomain":	"",
		"subdomain":	"d420c923-bbd7-4056-ab64-c3ca54c9b3cf",
		"allowfrom":	[]

We can now set up DNS accordingly. In this example, the response contains the subdomain d420c923-bbd7-4056-ab64-c3ca54c9b3cf and we want to set up the domain so that a certificate can be issued using acme-dns. CNAME

When the entries finished propagating we can install and issue a certificate using the acme-dns method. All settings will be saved and the certificate will be renewed automatically.

curl | sh

export ACMEDNS_USERNAME="eabcdb41-d89f-4580-826f-3e62e9755ef2"
export ACMEDNS_PASSWORD="pbAXVjlIOE01xbut7YnAbkhMQIkcwoHO0ek2j4Q0"
export ACMEDNS_SUBDOMAIN="d420c923-bbd7-4056-ab64-c3ca54c9b3cf"

~/ --issue --dns dns_acmedns -d

On success, the newly issued certificate will be located in ~/ which can be changed using the parameters --cert-file, --key-file, --ca-file and --fullchain-file. Using the parameter --reloadcmd we can issue a command on every future successful renewal.