Automatic Renewal of Wildcard Certificates

To automate the process of issuing and renewing TLS wildcard certificates we use - A pure Unix posh script implementing ACME client protocol. To complete the DNS challenge without manually adding TXT records, we are supported by the Namecheap API in this guide.


After the completion of this guide, we will be able to:

  1. Install and properly configure as well as setting up a DNS challenge through the Namecheap API
  2. Issue a wildcard certificate from a CA of our choice- we will use Let’s Encrypt in this guide
  3. Create an automatic renewal process for the certificate as well as reload our services after a successful renewal


With FreeBSD, it basically boils down to two options when installing The installation via the FreeBSD ports collection or using the installer.

Although I prefer the installation via the FreeBSD ports collection for maintenance reasons, it is of course possibly (and maybe preferred by others) to use the installer.

FreeBSD ports collection#

Login as root, update the ports collection and configure the port (if desired) for

cd /usr/ports
make update
cd security/
make config

If you want to change the default port configuration, now is your chance. I have disabled some default settings for which I have no use at the moment.

When you are done, it is time to build and install the port.

make install clean Installer#

If you decide to not use the FreeBSD ports collection (or run another OS) you can check out the installation via the installer here.

Please be advised, when you use the installer, your configuration files and certificates are in the home directory of the user you ran the installer with. This will be different from the paths used in the rest of this guide.


There are two types of configuration files used by


The first one is the global (or account) configuration. These settings are necessary (and valid) for every domain you issue. Lets have a look.

# /var/db/acme/

# needed if one uses DNS alias mode
# more information here:
# NSUPDATE_KEY="/var/db/acme/"


# Namecheap API settings

# Default CA Server

The NSUPDATE settings were disabled since no DNS alias mode is used.

Also the Namecheap API credentials have been added. More information on setting up the Namecheap API are found here. supports quite a lot different DNS API’s if you use a different provider.

The last configuration is setting your default / preferred CA’s server address. By default, uses ZeroSSL. In our example we use Let’s Encrypt instead. Changes can be made directly to the configuration file or by calling: --set-default-ca --server supports many different CA’s as well.


The second configuration affects only the specific certificate / domain and will be created when we issue our certificate. It is located in the directory: $CERT_HOME/domain.tld/domain.tld.conf where domain.tld is the name of your domain you issued your certificate for.


Since FreeBSD creates a new user / group for acme which has no permissions to write into /var/log, a log file must be manually created and the appropriate permissions must be set. This has to be only done once as user root.

touch /var/log/
chown acme:acme /var/log/

It is possible to setup newsyslogd for the logfiles. FreeBSD installs the necessary script. If desired, it can be enabled.

# /usr/local/etc/newsyslog.d/

# logfilename         [owner:group]   mode count size when  flags [/pid_file] [sig_num]
/var/log/  acme:acme       640  90    *    @T00   BC

Restart newsyslog after changes have been made.

service newsyslog restart

Issue certificates#

Now it is time to issue our certificate. Based on the issue command, adds options to your domain.conf. Since some of those options will be encrypted in your configuration file, it is advised to set them with your issue command.

First, login as user acme.

su acme

For my configuration, I issued my certificates with the following command. --issue \
--dns dns_namecheap \
--domain domain.tld -d *.domain.tld \
--reloadcmd "/var/db/acme/services.restart" \
--preferred-chain "ISRG Root X1" 

Parameters explained#

To go briefly trough the parameters:

  • issue - issues a new certificate
  • dns dns_namecheap - use a DNS challenge to verify the domain and use the DNS hook dns_namecheap. It is necessary to enable the Namecheap API and also setup the credentials in the account.conf as mentioned in the configuration part of this guide.
  • domain or -d - list of domains you want to issue certificates for. In this example a certificate for the root domain as well as every subdomain (wildcard) is issued.
  • reloadcmd - list of commands / scripts to be executed after a certificate is issued or renewed.
    I have used a script in my configuration to be more flexible for future applications. If you only use a webserver, a simple service restart will suffice here:
    reloadcmd "doas service nginx restart"

Reload script#

# /var/db/acme/services.restart

doas /usr/sbin/service nginx reload
doas /usr/sbin/service adguardhome restart

I use doas to gain super user access to restart services. Of course you can use any other means.

  • preferred-chain - select a specific certificate chain. In our case ISRG Root X1 from Let’s Encrypt.
    To use my own private DNS on my Android Phone, i had to select this specific chain from the CA. Here is some more information on certificate compatibility.

Certificate files#

The certificate(s) will be put in the previously defined $CERT_HOME directory:

  • $CERT_HOME/cert/domain.tld/domain.tld.key
  • $CERT_HOME/cert/domain.tld/fullchain.cer

Simply point your webserver or other services configuration to the appropriate path and reload / restart them manually.

Automatic Renewal#

To automatically renew all our previously issued certificates delivers a crontab script. To be more consistent to the FreeBSD system, I decided to add a system crontab for the user acme.


As user acme edit the crontab file with the following command:

crontab -e

And add the following line to your crontab file:

10 0 * * * /usr/local/sbin/ --cron --home "/var/db/acme/" > /dev/null

It will run the command in the cron mode every day at 0:10 system time. Dont worry about sending the outpout to /dev/null. still writes its logs to the predefined log directory.

Renewal test run#

You can test run the cron job with to confirm everything works as intended: --cron --test will go trough every certificate previously issued and will use the settings used to issue the certificate. For our example it will also use the Namecheap API for the DNS challenge and run our reload command.


We are now able to issue certificates, including wildcard ones. The use of a DNS API helps us to automatically run a DNS challenge for our domains without the need to manually add those TXT records. We can furthermore streamline the renewal process with the help of crontab files and reload commands.