ACME clients: how to choose one by class, not by name

12 min readUpdated

An ACME client is the software that gets certificates for you: it places an order with the certificate authority, proves control of each name and downloads the certificate [1]. Every client implements the same ACME protocol, defined in RFC 8555, so the authority runs the same checks whichever one asks [1]. What differs is where the client runs, whether it can put the certificate into your server, and what it does when something fails. Those three should decide the choice, and the clients named below are examples of each class.

Protocol
RFC 8555, the same for every client [1]
Classes
built into the server, a program run on a schedule, a library inside your application, a platform
Challenges
HTTP-01, DNS-01 and TLS-ALPN-01; which of them each client supports is in the table below
Wildcards
at Let's Encrypt only through DNS validation, so a record in your DNS has to change: by the client through your provider's API, or by hand [2]
Default key in our runs
ECDSA P-256 from Certbot 5.8.0, lego 5.5.2 and acme.sh 3.1.6 [7]

The four classes

  • Built into the server. The software that terminates connections obtains certificates itself, so the program that gets a certificate is the one that serves it. Caddy works this way: its documentation describes certificates provisioned for all your sites and kept renewed [5].

  • A separate program run on a schedule. A tool runs periodically, renews what is due, writes the files and reloads the server. It works with any server and has the most moving parts. Certbot and acme.sh are examples [2][3].

  • A library inside your own application. The application manages its own certificates, which suits software you distribute or services that create hostnames on the fly. lego is published as both an ACME client and an ACME library written in Go [4].

  • A platform that does it for you. Certificates are a property of the hosting or delivery layer, and nothing runs on your side. Bridge CDN works this way: Bridge SSL is the certificate layer of Bridge CDN, and certificates are issued and renewed automatically where your site is served. There is no file to install and no date to remember.

Clients compared

Within a class, clients differ in the challenges they support and in how the certificate reaches the server. The marker on each client's name is the source for its row; [7] marks what our runs showed.

ACME clients compared by class, challenges, deployment, default authority and license
ClientClassChallengesHow the certificate reaches the serverDefault authorityLicense
Caddy [5]built into the serverHTTP-01 and TLS-ALPN-01 on by default; DNS-01 with DNS provider credentials and a pluginCaddy is the web server and serves itLet's Encrypt, then a second ACME authority if that failsApache 2.0
Certbot [2]program on a scheduleHTTP-01; DNS-01 through DNS plugins or the manual pluginthe Apache and nginx plugins edit the server's configuration; certonly only saves the filesLet's EncryptApache 2.0
acme.sh [3]program on a scheduleHTTP-01 [7]; TLS-ALPN-01 with --alpn; DNS-01 through DNS APIs, aliases or by hand--install-cert copies key and chain to your paths and runs --reloadcmd [7]; its Apache and nginx modes do not edit your configurationan authority other than Let's Encrypt; --server picks anotherGPLv3
lego [4]library and command-line clientHTTP-01, DNS-01, TLS-ALPN-01writes files under .lego/certificates; --deploy-hook runs a command after each successful issue or renewal [7]Let's Encrypt [7]MIT
cert-manager [6]controller in a Kubernetes clusterHTTP01 and DNS01keeps the key and certificate in a Kubernetes Secret used by a Pod or an Ingress controllernone: each Issuer names its ACME serverApache 2.0

Certbot or acme.sh

Both run on the server as a scheduled program. They differ in how the certificate gets into the server. Certbot's Apache and nginx plugins change the server's configuration to use the certificate, and its guide asks you to point the server at the files in /etc/letsencrypt/live/ instead of copying them [2]. acme.sh does not edit the server's configuration: it copies the key and chain to the paths you name, and its README says the files in its own folder are for internal use only [3]. Certbot is most useful with root privileges, which let it configure Apache and nginx; acme.sh is a shell script that does not require root [2][3]. Check the authority too: the acme.sh README marks one other than Let's Encrypt as its default [3].

Whichever client you pick, it registers and keeps the ACME account, which is a protocol object of its own.

What each client left on disk

We issued one certificate for example.com and www.example.com with each command-line client, against Pebble, a local test authority, over HTTP-01 [7].

What Certbot, lego and acme.sh left on disk after one issuance
AspectCertbot 5.8.0lego 5.5.2acme.sh 3.1.6
Fileslive/example.com/: cert.pem, chain.pem, fullchain.pem, privkey.pem, each a link into archive/certificates/: example.com.crt, example.com.issuer.crt, example.com.key, example.com.jsonexample.com_ecc/: example.com.cer, ca.cer, fullchain.cer, example.com.key, the CSR and two .conf files
Certificate with its chainfullchain.pem, 2 certificatesexample.com.crt, 2 certificatesfullchain.cer, 2 certificates
KeyECDSA P-256, file mode 600ECDSA P-256ECDSA P-256
What the server should readthe live/ files themselves [2]example.com.crt and example.com.key [4]the copies --install-cert made [3]

A server given cert.pem alone sends no intermediate. Certbot's guide says that a server using cert.pem must also be given chain.pem, or some browsers will show errors [2].

Certbot: how it gets and installs a certificate

Certbot is the ACME client from EFF, the Electronic Frontier Foundation: it fetches a certificate from Let's Encrypt and can deploy it to a web server. It is meant to run on the web server itself and is most useful with root privileges, which let it configure Apache and nginx. For installation the documentation recommends the snap on Linux distributions that use systemd; the Docker image can obtain certificates but cannot install them into your web server, and pip is supported on a best-effort basis inside a virtual environment [2]. Running Certbot on your own server is one of the two routes to a Let's Encrypt certificate.

If a client you already use is Certbot and your server runs Windows, the page on Certbot on Windows sets out what its team has published about that platform.

Authenticators and installers

Certbot does its work through plugins. An authenticator proves control of the names and is always required; an installer edits the web server's configuration to use the certificate and is needed only if you want Certbot to do that:

  • --nginx and --apache authenticate over HTTP-01 and install [2].

  • --webroot writes the challenge file into the directory a running web server already serves [2].

  • --standalone runs its own HTTP server on port 80, so you may need to stop a web server that holds that port [2].

  • DNS plugins answer DNS-01 through your provider's API and are installed separately. DNS validation, by these plugins or the manual plugin, is the only way Certbot gets a wildcard certificate from Let's Encrypt [2].

Our pip-installed Certbot 5.8.0 listed two plugins, standalone and webroot, both authenticators, and no installer [7].

Obtain and install, or obtain only

Warning. Back up the nginx configuration before certbot --nginx: Certbot's guide recommends it, and certbot --nginx rollback reverts the plugin's changes [2].

$ sudo certbot --nginx -d example.com -d www.example.com
$ sudo certbot certonly --webroot -w /var/www/example -d example.com -d www.example.com

The first command uses the nginx plugin to obtain the certificate and install it; certbot on its own does the same as certbot run. The second only saves the files, and pointing the web server at them is yours. Files go to /etc/letsencrypt/live/<name>/, where the name is the one given to --cert-name or else the first domain [2]. Renewal timers, deploy hooks and dry runs are part of automating certificate renewal.

The questions that decide

Five questions decide between clients, in the order that removes options fastest.

  • What terminates your connections? If that software obtains certificates itself, as Caddy does [5], it is the first candidate, because nothing is left to deploy. If a platform terminates TLS, the question is already settled.

  • Can the client deploy the result? Obtaining a certificate is the easy half. Something must place the files and reload the service, and a client that cannot do it leaves you writing the missing step yourself. Certbot's installers and acme.sh's --install-cert do it in different ways [2][3].

  • Which challenge do you need? A wildcard needs DNS validation, so for unattended renewal the client must change your DNS through your provider's API. Check your provider against the client's list before you choose [2][3][4].

  • What happens when it fails? Whether failures are visible, whether they are retried, and whether anything reports them. A client that fails silently is no better than none until the certificate expires.

  • Who maintains it? Anything running unattended for years needs to keep receiving updates. This matters more than a feature comparison, and the project's release history answers it.

What the class decides for you

Each class has a characteristic failure and a characteristic strength.

A client built into the server cannot fail to deploy, because there is nothing to deploy. The cost is coupling: certificate behaviour is whatever that software implements, and changing servers means revisiting it.

A separate program works with anything, and pays for it in joints: the schedule, the challenge, the files and the reload can each stop on their own.

A library keeps certificates with the application, and makes their failures your code's responsibility.

On a platform none of that is yours, which is both the point and the limit: where it does something you need differently, there may be no setting to change.

Where the client should run

Where the client runs is easy to leave at the default, the server in front of you.

  • On the server that terminates connections is the obvious choice, and the right one when that server is long-lived and managed directly. The certificate is obtained where it will be used, and nothing has to travel.

  • On the machine that builds or deploys suits infrastructure rebuilt from images, where anything written onto a running server is lost at the next deployment. Here the certificate belongs in the deployment pipeline, not on the instance.

  • Somewhere central suits fleets: one place obtains certificates and distributes them, so the credentials for validation sit in one place instead of on every instance. In Kubernetes, cert-manager plays this role inside the cluster: it creates certificates for workloads and renews them before they expire [6].

What decides is what happens to the server. If it is replaced regularly, a client running on it is replaced too, and whatever it obtained goes with it. That mismatch gives you automation that worked once and then stopped.

What not to choose on

Three criteria look relevant to choosing a client and are not.

  • Feature counts. The protocol is the same. Long feature lists include integrations with particular DNS and hosting providers, such as Certbot's third-party plugins, the acme.sh DNS APIs and lego's DNS providers [2][3][4], and each of those matters only if you use that provider.

  • Popularity. How widely a client is used tells you nothing about whether it fits the way your connections are terminated, and that is the question that decides.

  • Speed of issuance. A renewal runs while the old certificate is still valid, so how long one issuance takes never reaches a visitor.

The one thing to verify before committing

Whether it can deploy to your setup, end to end, unattended.

Obtain a certificate with it, let it place the files and reload, then confirm from outside that the new certificate is the one being served. That single test separates a client that will work for years from one that needs a person at every renewal.

If the sequence needs a script you write to bridge a gap, that script is now part of your certificate infrastructure: keep it under version control, make its failures visible and make sure someone knows it exists.

FAQ

Does the choice of client affect the certificate?

Only through the request it sends: the names and the key are the client's choice, and the authority validates and signs by the same protocol whichever client asks. In our runs Certbot, lego and acme.sh all generated an ECDSA P-256 key by default. [1][7]

What is the most important criterion?

Whether the client can install the result into your setup without a person present. Every client obtains certificates; placing the files and reloading the service at every renewal is where they differ.

Should I use the client built into my server?

Yes, where one exists and supports the challenge you need. A server that obtains its own certificates removes deployment as a separate step, and a step that does not exist cannot fail silently.

Do I need a specific client for wildcards?

You need one that can change your DNS records unattended, because Let's Encrypt validates a wildcard only through DNS. That narrows the field to clients that support your DNS provider's API, and makes that API part of your certificate infrastructure. [2]

What is Certbot?

EFF's ACME client. It fetches a certificate from Let's Encrypt and can deploy it to a web server: its Apache and nginx plugins edit the server's configuration, and certbot certonly only saves the files. [2]

Does Certbot run on Windows?

Certbot's documentation lists Windows among its systems, with Administrator access required, but its changelog for version 2.10.0 says the beta Windows installer is no longer published. [2]

Sources

  1. RFC 8555, Automatic Certificate Management Environment (ACME): sections 4 and 7.

  2. Certbot documentation: Introduction, Get Certbot, User Guide (plugins, Where are my certificates?); Certbot changelog; LICENSE.txt.

  3. acme.sh README: supported CAs, modes, installing the certificate; LICENSE.md.

  4. lego documentation and README: features, obtaining certificates; LICENSE.

  5. Caddy documentation, Automatic HTTPS; Caddy README and LICENSE.

  6. cert-manager documentation: overview and the ACME issuer; LICENSE.

  7. Our runs: Certbot 5.8.0, lego 5.5.2 and acme.sh 3.1.6 against the Pebble 2.10.1 test authority on one machine, over HTTP-01; the files listed, the key types, Certbot's plugin list and the defaults printed by --help.