Installing and Using acme.sh for SSL Certificates (2025 Edition)
This guide covers the clean installation of acme.sh and how to request and manage SSL certificates. We’ll use a server-wide installation approach with an organized directory structure.
This tutorial assumes you’re logged in as root. If not, switch to root:
sudo -iWhy acme.sh?
acme.sh is a lightweight, pure bash ACME client that offers:
- Support for multiple CA providers (Let’s Encrypt, ZeroSSL, Google Trust Services)
- Extensive DNS provider support (100+ providers, including PowerDNS)
- Automatic certificate renewal
- Per-domain configuration with reload hooks
- No dependencies beyond standard bash/curl tools
Part 1: Clean Server-Wide Installation
Download acme.sh
Clone the repository to a standard system location:
git clone https://github.com/acmesh-official/acme.sh.git /usr/local/sbin/acmeSet Your Email Address
Define your email address for certificate notifications:
export ACME_EMAIL="your-email@example.com"Verify the variable:
echo $ACME_EMAILCreate Directory Structure
Create the necessary directories for acme.sh:
mkdir -p /etc/acme/{config,live,certs}What these directories are for:
/etc/acme/config/– Configuration and account data/etc/acme/live/– Your installed certificates (created manually per domain)/etc/acme/certs/– Raw certificates managed by acme.sh
Install acme.sh
Navigate to the installation directory and run the installer:
cd /usr/local/sbin/acme
./acme.sh --install \
--home /usr/local/sbin/acme \
--config-home /etc/acme/config \
--cert-home /etc/acme/certs \
--accountemail "$ACME_EMAIL" \
--no-profileKey flags explained:
--home: Location of acme.sh scripts--config-home: Configuration and account data--cert-home: SSL certificates storage--no-profile: Prevents adding aliases to shell config files (cleaner for server-wide installs)
Create Symlink for Easy Access
Make acme.sh available system-wide:
ln -s /usr/local/sbin/acme/acme.sh /usr/local/bin/acme.shNow you can run acme.sh from anywhere.
Upgrade to Latest Version
Ensure you have the latest version and enable automatic upgrades:
acme.sh --upgrade --auto-upgradeSwitch to Let’s Encrypt (Recommended)
By default, acme.sh uses ZeroSSL as the Certificate Authority. Let’s Encrypt is generally more reliable and widely used. Switch to Let’s Encrypt as your default CA:
acme.sh --set-default-ca --server letsencryptWhy Let’s Encrypt?
- More reliable and stable
- Larger user base and better community support
- Fewer timeout issues
- Industry standard for free SSL certificates
You can always switch back to ZeroSSL or use other CAs if needed (see Part 6).
Verify Installation
Check your installation:
acme.sh infoYou should see output similar to:
LE_WORKING_DIR=/usr/local/sbin/acme
LE_CONFIG_HOME=/etc/acme/config
CERT_HOME='/etc/acme/certs'
ACCOUNT_EMAIL='your-email@example.com'
AUTO_UPGRADE='1'Directory Structure
Your installation now has a clean structure:
/usr/local/sbin/acme/ → acme.sh scripts
/etc/acme/config/ → Configuration and account data
/etc/acme/certs/ → SSL certificates
/usr/local/bin/acme.sh → Symlink for easy accessPart 2: Set Up Automatic Renewal
Create a system cron job for automatic certificate renewal:
crontab -eAdd this line (runs daily at midnight):
0 0 * * * /usr/local/sbin/acme/acme.sh --cron --home /usr/local/sbin/acme --config-home /etc/acme/config > /dev/nullNote: The cron job checks all certificates daily and only renews those within 30 days of expiration.
Part 3: Requesting Certificates
Option 1: HTTP-01 Validation (Webroot)
This method requires your domain to be accessible via HTTP and you need write access to the webroot.
Create the ACME challenge directory:
mkdir -p /var/www/acme/.well-known/acme-challenge
chmod 755 /var/www/acmeRequest the certificate:
acme.sh --issue \
-d example.com \
-d www.example.com \
-w /var/www/acmeOption 2a: DNS-01 Validation (PowerDNS Example)
This method is ideal for wildcard certificates or when HTTP validation isn’t possible.
Set PowerDNS credentials:
export PDNS_Url="http://your-pdns-server:8081"
export PDNS_ServerId="localhost"
export PDNS_Token="your-api-key"
export PDNS_Ttl=60Request wildcard certificate:
acme.sh --issue \
-d example.com \
-d "*.example.com" \
--dns dns_pdnsThe credentials are automatically saved in /etc/acme/config/account.conf for future renewals.
Option 2b: DNS Manual Mode (No API Required)
If your DNS provider doesn’t have API support, you can use manual DNS mode. You’ll need to add TXT records manually to your DNS:
Request certificate in manual mode:
acme.sh --issue \
--dns \
-d example.com \
-d "*.example.com" \
--yes-I-know-dns-manual-mode-enough-go-ahead-pleaseacme.sh will output the TXT records you need to create:
Add the following TXT record:
Domain: '_acme-challenge.example.com'
TXT value: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx'After adding the TXT records to your DNS:
- Wait for DNS propagation (check with
dig _acme-challenge.example.com TXT) - Run the renew command to complete validation:
acme.sh --renew \
-d example.com \
--yes-I-know-dns-manual-mode-enough-go-ahead-pleaseImportant: Manual mode certificates do NOT auto-renew. You’ll need to add the TXT records manually each time. For production use, consider using a DNS provider with API support (see Part 7).
Option 3: Standalone Mode
If port 80 is available and you can temporarily stop your web server:
acme.sh --issue \
-d example.com \
--standalonePart 4: Installing Certificates to Live Directory
After requesting a certificate, install it to a specific directory with automatic reload:
Create Live Directory
mkdir -p /etc/acme/live/example.comInstall Certificate
acme.sh --install-cert -d example.com \
--key-file /etc/acme/live/example.com/privkey.pem \
--fullchain-file /etc/acme/live/example.com/fullchain.pem \
--reloadcmd "systemctl reload nginx"What this does:
- Copies the private key to
/etc/acme/live/example.com/privkey.pem - Copies the full certificate chain to
/etc/acme/live/example.com/fullchain.pem - Sets up automatic reload command (runs after each renewal)
- Saves this configuration for future automatic renewals
Replace the reload command with whatever is appropriate for your setup:
- Nginx:
systemctl reload nginx - Apache:
systemctl reload apache2 - Postfix:
systemctl reload postfix - Custom script:
/path/to/your/reload-script.sh
Verify Installation
Check that certificates were created:
ls -lh /etc/acme/live/example.com/You should see:
-rw------- 1 root root 227 Oct 29 12:00 privkey.pem
-rw------- 1 root root 3.6K Oct 29 12:00 fullchain.pemPart 5: Managing Certificates
List All Certificates
View all issued certificates:
acme.sh --listExample output:
Main_Domain KeyLength SAN_Domains CA Created Renew
example.com ec-256 www.example.com LetsEncrypt.org 2025-10-29T10:15:22Z 2025-12-27T10:15:22ZView Certificate Details
Get detailed information about a specific certificate:
acme.sh --info -d example.comManual Certificate Renewal
To manually renew a specific certificate:
acme.sh --renew -d example.com --forceTo renew all certificates:
acme.sh --renew-allTest Renewal Without Actually Renewing
acme.sh --renew -d example.com --force --testPart 6: Switching Certificate Authorities
In the initial setup, we switched to Let’s Encrypt as the default CA. Here’s how to manage CAs:
View Available CAs
acme.sh --list-serversChange Default CA
Switch to ZeroSSL:
bash
acme.sh --set-default-ca --server zerosslOther available CAs:
- Let’s Encrypt:
letsencrypt(recommended, set in initial setup) - ZeroSSL:
zerossl(acme.sh default) - Google Trust Services:
google - Buypass:
buypass
Issue Certificate with Specific CA
Override the default for a single certificate:
acme.sh --issue -d example.com --server zerossl -w /var/www/acmePart 7: DNS Providers
acme.sh supports 100+ DNS providers. Here are some common examples:
Cloudflare
export CF_Token="your-cloudflare-api-token"
export CF_Account_ID="your-account-id"
acme.sh --issue -d example.com --dns dns_cfRoute53 (AWS)
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
acme.sh --issue -d example.com --dns dns_awsDigitalOcean
export DO_API_KEY="your-api-key"
acme.sh --issue -d example.com --dns dns_dgonSee the full DNS provider list in the acme.sh wiki.
Part 8: Certificate Types
ECC Certificates (Default since v3.0.6)
Since acme.sh v3.0.6, ECC certificates are the default. Elliptic Curve certificates are smaller, faster, and more secure:
# ec-256 is the default (no flag needed)
acme.sh --issue -d example.com -w /var/www/acme
# or explicitly specify ec-256
acme.sh --issue -d example.com -w /var/www/acme --keylength ec-256
# for higher security
acme.sh --issue -d example.com -w /var/www/acme --keylength ec-384RSA Certificates (Legacy)
Use RSA only if you need compatibility with very old clients:
acme.sh --issue -d example.com -w /var/www/acme --keylength 2048 # 2048-bit RSA
acme.sh --issue -d example.com -w /var/www/acme --keylength 4096 # 4096-bit RSAPart 9: Common Use Cases
Multiple Domains on One Certificate
acme.sh --issue \
-d example.com \
-d www.example.com \
-d mail.example.com \
-d ftp.example.com \
-w /var/www/acmeWildcard Certificate
Requires DNS validation:
acme.sh --issue \
-d example.com \
-d "*.example.com" \
--dns dns_cfSeparate Certificates for Each Domain
# Certificate for example.com
acme.sh --issue -d example.com -w /var/www/acme
acme.sh --install-cert -d example.com \
--key-file /etc/acme/live/example.com/privkey.pem \
--fullchain-file /etc/acme/live/example.com/fullchain.pem \
--reloadcmd "systemctl reload nginx"
# Certificate for example.net
acme.sh --issue -d example.net -w /var/www/acme
acme.sh --install-cert -d example.net \
--key-file /etc/acme/live/example.net/privkey.pem \
--fullchain-file /etc/acme/live/example.net/fullchain.pem \
--reloadcmd "systemctl reload nginx"Part 10: Troubleshooting
Common Issues
1. Timeout from CA
If you experience timeouts or connection issues:
# Try a different CA (if you're not already using Let's Encrypt)
acme.sh --set-default-ca --server letsencrypt
# Or switch to ZeroSSL
acme.sh --set-default-ca --server zerossl2. Rate limit exceeded
Let’s Encrypt limits: 50 certificates per domain per week.
Solution: Use staging server for testing:
acme.sh --issue -d example.com --staging -w /var/www/acme2. CAA record error
Add a CAA DNS record:
example.com. CAA 0 issue "letsencrypt.org"3. Domain validation fails
Use Let’s Debug to diagnose issues.
4. Certificate not renewed automatically
Check cron job:
crontab -l | grep acmeTest renewal manually:
acme.sh --cron --force5. Reload command not working
Check reload command in certificate info:
acme.sh --info -d example.com | grep ReloadCmdUpdate if needed:
acme.sh --install-cert -d example.com \
--reloadcmd "systemctl reload nginx"Debug Mode
Enable debug output:
acme.sh --issue -d example.com -w /var/www/acme --debugFor more verbose output:
acme.sh --issue -d example.com -w /var/www/acme --debug 2Check Logs
View acme.sh logs:
tail -f /etc/acme/config/acme.sh.logImportant: OCSP Deprecation (2025)
Let’s Encrypt ended OCSP support in 2025. Here’s what you need to know:
What Changed
- Do NOT use
--ocsp-must-stapleflag – it will cause certificate issuance to fail - Certificate Revocation Lists (CRLs) now replace OCSP
- No action required for most users – certificates work the same way
Timeline
- January 30, 2025: OCSP Must-Staple requests began failing
- May 7, 2025: OCSP URLs removed from all new certificates
- August 6, 2025: OCSP responders completely shut down
If you have old certificates with OCSP Must-Staple extension, simply renew them without the flag.
Maintenance
Updating acme.sh
acme.sh updates automatically if auto-upgrade is enabled. To manually update:
acme.sh --upgradeCheck current version:
acme.sh --versionBackup Configuration
Regularly backup your acme.sh configuration:
tar -czf /backup/acme-$(date +%Y%m%d).tar.gz \
/etc/acme/config \
/etc/acme/live \
/usr/local/sbin/acmeMigrating to New Server
- Backup on old server:
tar -czf acme-backup.tar.gz /etc/acme /usr/local/sbin/acme- Transfer and restore on new server:
tar -xzf acme-backup.tar.gz -C /
ln -s /usr/local/sbin/acme/acme.sh /usr/local/bin/acme.sh- Update cron job on new server
Quick Reference
Common Commands
# List all certificates
acme.sh --list
# Get certificate info
acme.sh --info -d example.com
# Issue new certificate (HTTP)
acme.sh --issue -d example.com -w /var/www/acme
# Issue new certificate (DNS)
acme.sh --issue -d example.com --dns dns_cf
# Issue certificate (DNS manual mode)
acme.sh --issue --dns -d example.com -d "*.example.com" --yes-I-know-dns-manual-mode-enough-go-ahead-please
# Then after adding TXT records:
acme.sh --renew -d example.com --yes-I-know-dns-manual-mode-enough-go-ahead-please
# Install certificate to live directory
acme.sh --install-cert -d example.com \
--key-file /etc/acme/live/example.com/privkey.pem \
--fullchain-file /etc/acme/live/example.com/fullchain.pem \
--reloadcmd "systemctl reload nginx"
# Renew specific certificate
acme.sh --renew -d example.com
# Renew all certificates
acme.sh --renew-all
# Force renewal (testing)
acme.sh --renew -d example.com --force
# Test renewal without renewing
acme.sh --renew -d example.com --force --test
# Check renewal cron
crontab -l | grep acme
# Switch to Let's Encrypt
acme.sh --set-default-ca --server letsencrypt
# Enable debug mode
acme.sh --issue -d example.com --debug
# Upgrade acme.sh
acme.sh --upgradeDirectory Structure
/usr/local/sbin/acme/ → acme.sh installation
/etc/acme/config/ → Configuration files
/etc/acme/certs/ → Raw certificates (managed by acme.sh)
/etc/acme/live/example.com/ → Installed certificates (your symlinks)
├── privkey.pem → Private key
└── fullchain.pem → Full certificate chainCertificate File Locations
After installation with --install-cert:
- Private Key:
/etc/acme/live/example.com/privkey.pem - Certificate + Chain:
/etc/acme/live/example.com/fullchain.pem
Use these paths in your web server, mail server, or other service configuration.
Example: Complete Workflow
Here’s a complete example from installation to working certificate:
# 1. Install acme.sh
git clone https://github.com/acmesh-official/acme.sh.git /usr/local/sbin/acme
mkdir -p /etc/acme/{config,live,certs}
cd /usr/local/sbin/acme
./acme.sh --install \
--home /usr/local/sbin/acme \
--config-home /etc/acme/config \
--cert-home /etc/acme/certs \
--accountemail "admin@example.com" \
--no-profile
ln -s /usr/local/sbin/acme/acme.sh /usr/local/bin/acme.sh
# Switch to Let's Encrypt as default CA
acme.sh --set-default-ca --server letsencrypt
# 2. Set up cron job
crontab -e
# Add: 0 0 * * * /usr/local/sbin/acme/acme.sh --cron --home /usr/local/sbin/acme --config-home /etc/acme/config > /dev/null
# 3. Request certificate
mkdir -p /var/www/acme/.well-known/acme-challenge
acme.sh --issue \
-d example.com \
-d www.example.com \
-w /var/www/acme
# 4. Install to live directory
mkdir -p /etc/acme/live/example.com
acme.sh --install-cert -d example.com \
--key-file /etc/acme/live/example.com/privkey.pem \
--fullchain-file /etc/acme/live/example.com/fullchain.pem \
--reloadcmd "systemctl reload nginx"
# 5. Verify
ls -lh /etc/acme/live/example.com/
acme.sh --info -d example.comDone! Your certificate will now renew automatically every 60 days.
