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 -i

Why 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/acme

Set Your Email Address

Define your email address for certificate notifications:

export ACME_EMAIL="your-email@example.com"

Verify the variable:

echo $ACME_EMAIL

Create 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-profile

Key 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.sh

Now 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-upgrade

Switch 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 letsencrypt

Why 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 info

You 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 access

Part 2: Set Up Automatic Renewal

Create a system cron job for automatic certificate renewal:

crontab -e

Add 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/null

Note: 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/acme

Request the certificate:

acme.sh --issue \
  -d example.com \
  -d www.example.com \
  -w /var/www/acme

Option 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=60

Request wildcard certificate:

acme.sh --issue \
  -d example.com \
  -d "*.example.com" \
  --dns dns_pdns

The 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-please

acme.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:

  1. Wait for DNS propagation (check with dig _acme-challenge.example.com TXT)
  2. Run the renew command to complete validation:
acme.sh --renew \
  -d example.com \
  --yes-I-know-dns-manual-mode-enough-go-ahead-please

Important: 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 \
  --standalone

Part 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.com

Install 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.pem

Part 5: Managing Certificates

List All Certificates

View all issued certificates:

acme.sh --list

Example 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:22Z

View Certificate Details

Get detailed information about a specific certificate:

acme.sh --info -d example.com

Manual Certificate Renewal

To manually renew a specific certificate:

acme.sh --renew -d example.com --force

To renew all certificates:

acme.sh --renew-all

Test Renewal Without Actually Renewing

acme.sh --renew -d example.com --force --test

Part 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-servers

Change Default CA

Switch to ZeroSSL:

bash

acme.sh --set-default-ca --server zerossl

Other 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/acme

Part 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_cf

Route53 (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_aws

DigitalOcean

export DO_API_KEY="your-api-key"

acme.sh --issue -d example.com --dns dns_dgon

See 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-384

RSA 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 RSA

Part 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/acme

Wildcard Certificate

Requires DNS validation:

acme.sh --issue \
  -d example.com \
  -d "*.example.com" \
  --dns dns_cf

Separate 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 zerossl

2. 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/acme

2. 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 acme

Test renewal manually:

acme.sh --cron --force

5. Reload command not working

Check reload command in certificate info:

acme.sh --info -d example.com | grep ReloadCmd

Update 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 --debug

For more verbose output:

acme.sh --issue -d example.com -w /var/www/acme --debug 2

Check Logs

View acme.sh logs:

tail -f /etc/acme/config/acme.sh.log

Important: 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-staple flag – 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 --upgrade

Check current version:

acme.sh --version

Backup 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/acme

Migrating to New Server

  1. Backup on old server:
   tar -czf acme-backup.tar.gz /etc/acme /usr/local/sbin/acme
  1. 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
  1. 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 --upgrade

Directory 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 chain

Certificate 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.com

Done! Your certificate will now renew automatically every 60 days.

Similar Posts

  • | |

    Generate SSL certificates with acme.sh on Nginx.

    In this article, we will see how to install and configure “acme.sh” to generate SSL certificates for domains and how to implement it with Nginx to secure the connection to corresponding websites hosted on our web server via “HTTPS”. To optimize the security of connections to the web server and comply with all applicable guidelines,…

  • Remove domain from list of certificates in acme.sh.

    It often happens that a domain is moved to another web server or is simply no longer registered and the corresponding certificate needs to be removed from the list of domains that acme.sh maintains. This can be done easily with the following command: # acme.sh –remove -d my_domain.com [Wed Feb  1 15:10:58 CEST 2022] my_domain.com…