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#
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-apacheDebian / 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 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 registers automatically on first use. No separate registration step is required.
sudo acme.sh --register-account \
--server https://acme-proxy.example.com/acme/acme/directory \
--email admin@example.com \
--home /etc/acme.shLego 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.
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’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 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