ACME Clients#
This guide covers installation and system-level configuration of ACME clients for use with acme-proxy. It is intended for system administrators deploying certificate automation on behalf of end users.
For certificate issuance commands and per-scenario usage, see user.md.
Table of Contents#
Installing ACME Clients#
Certbot#
Note: Certbot’s actively maintained distribution is via Snap. The
.debpackages available in apt repositories are no longer maintained by the Certbot project and ship outdated versions.
Install via Snap:
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/local/bin/certbotThe Snap package installs certbot.timer and certbot.service systemd units automatically.
For the NGINX plugin:
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-nginxFor the Apache plugin:
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-apacheacme.sh#
Debian / Ubuntu:
sudo apt-get update
sudo apt-get install -y acme.sh socatRHEL / Rocky / AlmaLinux — requires EPEL:
sudo dnf install -y epel-release
sudo dnf install -y acme.sh socatThe package installs the binary to /usr/bin/acme.sh. Use /etc/acme.sh as the configuration home for system-wide installations (passed via --home in all commands).
socatis required for standalone mode. acme.sh usessocatto bind port 80 for HTTP-01 challenges in standalone mode. It is installed above alongside acme.sh. This is not required if you use NGINX or Apache plugin mode.
Lego#
Lego has no official packages in major Linux distribution repositories. Install the release binary directly:
LEGO_VERSION=4.33.0
curl -fsSL "https://github.com/go-acme/lego/releases/download/v${LEGO_VERSION}/lego_v${LEGO_VERSION}_linux_amd64.tar.gz" \
| tar xz lego
sudo install -o root -g root -m 0755 lego /usr/local/bin/lego
rm lego
lego --versionVerify the checksum from the GitHub releases page before deploying to production. Pin
LEGO_VERSIONin your configuration management tool and treat upgrades as a deliberate change.
Account Registration#
Each ACME client must register an account with acme-proxy before any certificates can be issued. This is a one-time step per host.
Certbot#
Certbot registers automatically on first use. No separate registration step is required.
acme.sh#
sudo acme.sh --register-account \
--server https://acme-proxy.example.com/acme/acme/directory \
--email admin@example.com \
--home /etc/acme.shLego#
Lego registers automatically on the first run invocation. No separate registration step is required.
Configuring Auto-Renewal via Systemd#
Replacing cron-based renewal with systemd timers provides:
- Missed-run recovery via
Persistent=true— if the system was off at the scheduled time, the timer fires on next boot. - Structured log output to the systemd journal, queryable and forwardable to syslog.
- Visibility via
systemctl list-timers.
All service units below set SyslogIdentifier so logs can be filtered by tag regardless of which syslog daemon is in use.
Certbot#
The Snap-installed certbot ships certbot.timer and certbot.service units automatically. Enable the timer and confirm it is active:
sudo systemctl enable --now certbot.timer
systemctl status certbot.timerConfigure the SyslogIdentifier so certbot’s renewal logs are tagged consistently alongside other ACME clients:
sudo mkdir -p /etc/systemd/system/certbot.service.d
sudo tee /etc/systemd/system/certbot.service.d/logging.conf <<'EOF'
[Service]
StandardOutput=journal
StandardError=journal
SyslogIdentifier=certbot-renewal
EOF
sudo systemctl daemon-reloadTest renewal without issuing:
sudo certbot renew --dry-runacme.sh#
acme.sh’s --cron flag iterates over all configured certificates and renews those expiring within 30 days. A single service and timer unit covers all certificates on the host.
Create the service unit:
sudo tee /etc/systemd/system/acme-renewal.service <<'EOF'
[Unit]
Description=Renew ACME certificates (acme.sh)
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/bin/acme.sh --cron --home /etc/acme.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=acme-renewal
EOFCreate the timer unit:
sudo tee /etc/systemd/system/acme-renewal.timer <<'EOF'
[Unit]
Description=Daily ACME certificate renewal check (acme.sh)
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
EOFEnable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now acme-renewal.timerVerify:
systemctl status acme-renewal.timer
systemctl list-timers acme-renewal.timerLego#
Lego has no built-in renewal scheduling. Create service and timer units manually.
Unlike acme.sh and certbot, lego’s renew command targets one domain at a time. If you manage multiple certificates, use a wrapper script.
Wrapper script for multiple certificates:
sudo tee /usr/local/sbin/lego-renew-all.sh <<'EOF'
#!/bin/bash
set -euo pipefail
LEGO=/usr/local/bin/lego
SERVER=https://acme-proxy.example.com/acme/acme/directory
EMAIL=admin@example.com
# Add each managed domain below
domains=(
myserver.example.com
anotherserver.example.com
)
for domain in "${domains[@]}"; do
"$LEGO" \
--server "$SERVER" \
--accept-tos \
--email "$EMAIL" \
--http \
-d "$domain" \
renew
done
EOF
sudo chmod 0700 /usr/local/sbin/lego-renew-all.shCreate the service unit:
sudo tee /etc/systemd/system/lego-renewal.service <<'EOF'
[Unit]
Description=Renew ACME certificates (lego)
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/sbin/lego-renew-all.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=lego-renewal
EOFCreate the timer unit:
sudo tee /etc/systemd/system/lego-renewal.timer <<'EOF'
[Unit]
Description=Daily ACME certificate renewal check (lego)
[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true
[Install]
WantedBy=timers.target
EOFEnable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now lego-renewal.timerVerify:
systemctl status lego-renewal.timer
systemctl list-timers lego-renewal.timerLog Management#
All service units above write to the systemd journal with a unique SyslogIdentifier. Logs are accessible via journalctl and forwarded to syslog if your system runs rsyslog or syslog-ng with journal forwarding enabled.
Filter renewal logs by client:
| Client | Command |
|---|---|
| Certbot | journalctl -t certbot-renewal |
| acme.sh | journalctl -t acme-renewal |
| Lego | journalctl -t lego-renewal |
Follow logs in real time:
journalctl -t acme-renewal -fForward to syslog — rsyslog:
Ensure the journal input module is loaded in /etc/rsyslog.conf or a drop-in under /etc/rsyslog.d/:
module(load="imjournal" StateFile="imjournal.state")Forward to syslog — syslog-ng:
Ensure the systemd-journal source is present in your syslog-ng configuration:
source s_systemd { systemd-journal(); };Log retention:
Journal retention is controlled by /etc/systemd/journald.conf. Set MaxRetentionSec and SystemMaxUse appropriate to your environment:
[Journal]
SystemMaxUse=500M
MaxRetentionSec=90dayApply changes with:
sudo systemctl restart systemd-journald