# acme.sh User Guide

`acme.sh` is an [ACME protocol](https://datatracker.ietf.org/doc/html/rfc8555) client written **purely in Unix shell**. It issues, renews, and installs free TLS/SSL certificates from ZeroSSL, Let's Encrypt, Google, SSL.com, Actalis, and any RFC 8555-compliant Certificate Authority (CA) — with **no dependency on Python, Go, or any compiled runtime**. If you have a POSIX shell, `curl` (or `wget`), `openssl`, and `cron`, you can run it.

This guide takes you from a clean machine to fully automated, self-renewing certificates. Written from the `acme.sh` source.

> [!NOTE]
> `acme.sh` does **not** require `root` or `sudo`. It installs entirely inside your home directory (`~/.acme.sh/`) and renews certificates as your own user. Running as root is supported and is only needed for privileged modes (standalone on port 80/443, Apache/Nginx integration, writing certs into system paths).

---

## 1. Quick Start

Three commands take you from nothing to a live, auto-renewing certificate using the webroot of an existing website.

```bash
# 1. Install acme.sh into ~/.acme.sh and register an account
curl https://get.acme.sh | sh -s email=[email removed]

# 2. Open a new shell so the `acme.sh` alias is available, then issue a cert
acme.sh --issue -d example.com -w /var/www/html

# 3. Copy the cert into your web server and tell acme.sh how to reload it
acme.sh --install-cert -d example.com \
  --key-file       /etc/nginx/ssl/example.com.key \
  --fullchain-file /etc/nginx/ssl/example.com.cer \
  --reloadcmd      "service nginx force-reload"
```

```text
[Wed Jun  4 00:00:00 UTC 2026] Cert success.
[Wed Jun  4 00:00:00 UTC 2026] Your cert is in: /root/.acme.sh/example.com/example.com.cer
[Wed Jun  4 00:00:00 UTC 2026] Installing key to: /etc/nginx/ssl/example.com.key
[Wed Jun  4 00:00:00 UTC 2026] Running reload cmd: service nginx force-reload
[Wed Jun  4 00:00:00 UTC 2026] Reload successful
```

That is the whole lifecycle. The installer also created a daily `cron` job, so the certificate renews itself indefinitely — you never have to touch it again.

> [!IMPORTANT]
> Always deliver the certificate to your server with `--install-cert` and a `--reloadcmd`. **Do not** point your web server directly at the files in `~/.acme.sh/`. Those are internal working files; their layout can change, and a copy there will silently go stale after a renewal.

---

## 2. Installation

### 2.1 Requirements

| Requirement | Notes |
|---|---|
| POSIX shell | `bash`, `dash`, `ash`, or plain `sh`. Detected and used automatically. |
| `openssl` | The cryptographic workhorse — key generation, CSR, signing. **Required.** |
| `curl` **or** `wget` | For ACME / DNS API HTTP calls. At least one is required. |
| `socat` | Only for **standalone** mode (built-in web server). Not needed otherwise. |
| `crontab` | For automatic renewal. Optional but strongly recommended. |
| `idn` | Only if you issue certs for Internationalized Domain Names (IDN). |

`acme.sh` is tested on Linux (Debian, Ubuntu, Alpine, Arch, Fedora, openSUSE, Gentoo, Kali, Oracle Linux, Mageia, and more), macOS, FreeBSD, OpenBSD, NetBSD, DragonFlyBSD, Solaris/illumos, Windows (Cygwin), Haiku, and OpenWRT.

### 2.2 Install methods

=== "Install online (recommended)"

    ```bash
    curl https://get.acme.sh | sh -s email=[email removed]
    ```

    Or with `wget`:

    ```bash
    wget -O - https://get.acme.sh | sh -s email=[email removed]
    ```

=== "Install from Git"

    ```bash
    git clone https://github.com/acmesh-official/acme.sh.git
    cd ./acme.sh
    ./acme.sh --install -m [email removed]
    ```

=== "Run in Docker"

    ```bash
    docker run --rm -it \
      -v "$(pwd)/acme.sh-data":/acme.sh \
      neilpang/acme.sh --version
    ```

The installer performs exactly three actions:

1. Copies `acme.sh` into your home directory at `~/.acme.sh/` (this is also where all certificates are stored).
2. Creates a shell alias so you can type `acme.sh` from anywhere: `acme.sh=~/.acme.sh/acme.sh`.
3. Installs a daily `cron` job that checks and renews certificates as needed:
   ```text
   0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
   ```

> [!WARNING]
> After installing, **close your terminal and open a new one** so the `acme.sh` alias takes effect. If `acme.sh: command not found` appears, this is almost always the cause.

### 2.3 Verify the install

```bash
acme.sh --version
```

```text
https://github.com/acmesh-official/acme.sh
v3.1.4
```

```bash
acme.sh -h        # full list of commands and parameters
```

> [!TIP]
> Prefer not to install the cron job or shell alias (e.g. you manage scheduling yourself)? Add `--nocron` and/or `--noprofile` to the `--install` command.

---

## 3. Choosing a Validation Mode

Before issuing a certificate, the CA must verify you control the domain. `acme.sh` supports every standard ACME challenge plus several convenience wrappers. Pick the row that matches your situation.

| Mode | Flag | Needs root? | Best for |
|---|---|---|---|
| **Webroot** | `-w <dir>` | No | A server already serving a website over HTTP |
| **Standalone** | `--standalone` | Yes (port 80) | A box with no web server running |
| **Standalone ALPN** | `--alpn` | Yes (port 443) | Port 80 blocked, port 443 free |
| **Apache** | `--apache` | Yes | An Apache server (no config edits made) |
| **Nginx** | `--nginx` | Yes | An Nginx server (config auto-restored after) |
| **DNS API** | `--dns <hook>` | No | Wildcards; servers with no public HTTP; **fully automatic** |
| **DNS manual** | `--dns` | No | A DNS provider with no API (manual TXT entry) |
| **DNS persist** | `--dns-persist` | No | Unattended renewal without DNS API (single long-lived record) |
| **Stateless** | `--stateless` | No | Behind a reverse proxy / many front-ends |

> [!TIP]
> For wildcard certificates (`*.example.com`) you **must** use a DNS-based mode — HTTP challenges cannot prove control of a wildcard. DNS API mode is the only one that renews wildcards with zero manual work.

---

## 4. Issuing Certificates

The certificate's **main domain** (the first `-d`) doubles as its name — you'll use it later to renew, install, or remove the cert. Certificates default to **ECDSA P-256** (`ec-256`) keys and are stored under `~/.acme.sh/<main-domain>/`.

### 4.1 Webroot mode

Point `-w` at the document root of a site you already serve over HTTP on port 80.

```bash
acme.sh --issue -d example.com -w /var/www/html
```

Add more `-d` flags for a multi-domain (SAN) certificate. All domains must resolve to this same server and share the webroot:

```bash
acme.sh --issue \
  -d example.com -d www.example.com -d cp.example.com \
  -w /var/www/html
```

> [!NOTE]
> You must have **write access** to the webroot directory — `acme.sh` writes a temporary challenge file under `<webroot>/.well-known/acme-challenge/` and removes it afterward.

### 4.2 Standalone mode

When nothing is serving HTTP, let `acme.sh` run its own temporary web server. Requires `socat` and the ability to bind the port.

```bash
# Temporary server on port 80 (HTTP-01)
acme.sh --issue --standalone -d example.com

# Temporary server on port 443 (TLS-ALPN-01) — use when port 80 is blocked
acme.sh --issue --alpn -d example.com
```

If your machine sits behind a load balancer or reverse proxy that forwards a non-standard port, set the listening port explicitly:

```bash
acme.sh --issue --standalone -d example.com --httpport 8080
```

### 4.3 Apache / Nginx mode

If you run Apache or Nginx, these modes verify the domain **without writing to your webroot and without permanently changing your server config** — Nginx mode temporarily reconfigures the server and restores it afterward.

```bash
acme.sh --issue --apache -d example.com
acme.sh --issue --nginx  -d example.com
```

> [!IMPORTANT]
> Apache/Nginx mode only **issues** the certificate — it does not wire the cert into your site. You still configure your vhost to use the cert (see [§6, Installing the Certificate](#6-installing-the-certificate-into-your-server)).

### 4.4 DNS API mode (fully automatic, supports wildcards)

If your DNS provider has an API, `acme.sh` can create and delete the validation TXT records for you — including for wildcards — and do it again automatically at every renewal. This is the recommended mode for most modern setups.

The pattern is always the same:

1. Export the credential environment variables for your provider.
2. Run `--issue --dns <hookname>`.

Example for **Cloudflare** (`dns_cf`):

```bash
export CF_Token="your-cloudflare-api-token"
export CF_Account_ID="your-account-id"

acme.sh --issue --dns dns_cf -d example.com -d '*.example.com'
```

```text
[Wed Jun  4 00:00:00 UTC 2026] Adding txt value for domain: _acme-challenge.example.com
[Wed Jun  4 00:00:00 UTC 2026] Added, OK
[Wed Jun  4 00:00:00 UTC 2026] Let's check the txt records, DNS is propagating...
[Wed Jun  4 00:00:00 UTC 2026] Cert success.
```

> [!TIP]
> You only need to export the credentials **once**. On first use, `acme.sh` saves them into its account configuration (`~/.acme.sh/account.conf`) so renewals run unattended — you do not have to re-export them in the cron environment.

`acme.sh` ships **178 DNS provider plugins**. A few of the most common, with their required variables:

| Provider | Hook | Required environment variables |
|---|---|---|
| Cloudflare | `dns_cf` | `CF_Token` + `CF_Account_ID` (or legacy `CF_Key` + `CF_Email`) |
| AWS Route 53 | `dns_aws` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` |
| Google Cloud DNS | `dns_gcloud` | (uses `gcloud` CLI auth) |
| Azure DNS | `dns_azure` | `AZUREDNS_SUBSCRIPTIONID`, `AZUREDNS_TENANTID`, `AZUREDNS_APPID`, `AZUREDNS_CLIENTSECRET` |
| DigitalOcean | `dns_dgon` | `DO_API_KEY` |
| GoDaddy | `dns_gd` | `GD_Key`, `GD_Secret` |
| Namecheap | `dns_namecheap` | `NAMECHEAP_API_KEY`, `NAMECHEAP_USERNAME`, `NAMECHEAP_SOURCEIP` |
| DuckDNS | `dns_duckdns` | `DuckDNS_Token` |

> [!NOTE]
> To find the exact variables for any provider, open its plugin file `dnsapi/dns_<provider>.sh` — each begins with a self-documenting `Options:` block listing every variable it reads. Full per-provider documentation lives at the [dnsapi wiki](https://github.com/acmesh-official/acme.sh/wiki/dnsapi).

### 4.5 DNS manual mode

If your provider has no API, you can add the TXT records by hand. Note the warning: **manual mode cannot auto-renew.**

```bash
acme.sh --issue --dns -d example.com -d www.example.com
```

```text
Add the following txt record:
Domain:_acme-challenge.example.com
Txt value:9ihDbjYfTExAYeDs4DBUeuTo18KBzwvTEjUnSwd32-c

Please add those txt records to the domains. Waiting for the dns to take effect.
```

Add each TXT record at your DNS provider, wait for propagation, then complete issuance:

```bash
acme.sh --renew -d example.com
```

> [!CAUTION]
> DNS manual mode is a one-time, hands-on operation. Every renewal requires you to add a fresh TXT record by hand. For unattended renewal without an API, use **DNS persist mode** (§4.6) instead.

### 4.6 DNS persist mode

DNS persist mode (an implementation of [draft-ietf-acme-dns-persist-01](https://datatracker.ietf.org/doc/draft-ietf-acme-dns-persist/)) lets you place **one long-lived `_validation-persist` TXT record** in your zone and reuse it for every future issuance and renewal — no per-issuance token, so renewals need no DNS edits.

```bash
# Step 1: print the TXT record to add (and pick a CA)
acme.sh --make-dns-persist-value -d example.com --server letsencrypt
```

```text
TXT persist domain:_validation-persist.example.com
TXT persist value :"letsencrypt.org; accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/123456789"
```

Add that single TXT record at your DNS provider, wait for propagation, then issue:

```bash
# Step 2: issue using the persistent record
acme.sh --issue -d example.com --dns-persist
```

Optional flags for `--make-dns-persist-value`:

| Flag | Effect |
|---|---|
| `--dns-persist-wildcard` | Authorizes wildcard / subdomain certificates from the same record. |
| `--dns-persist-ca-name <name>` | Pin a specific CA identity domain (e.g. `ssl.com`). |
| `--dns-persist-days <N>` | Self-expire the record `N` days from now. |

### 4.7 Choosing the key type (ECC or RSA)

The default is `ec-256`. Override with `-k` / `--keylength`:

| Value | Key |
|---|---|
| `ec-256` | ECDSA P-256 — **default**, fast and widely supported |
| `ec-384` | ECDSA P-384 |
| `ec-521` | ECDSA P-521 (not accepted by Let's Encrypt) |
| `2048` / `3072` / `4096` / `8192` | RSA of that bit length |

```bash
acme.sh --issue -d example.com -w /var/www/html --keylength 4096   # RSA 4096
```

> [!TIP]
> You can issue **both** an ECC and an RSA certificate for the same domain. They are stored separately; the RSA/ECC cert lives in `~/.acme.sh/example.com_ecc/` vs `~/.acme.sh/example.com/`. Add `--ecc` to later commands to act on the ECC copy.

---

## 5. Selecting a Certificate Authority

ZeroSSL is the default CA. Switch per-issuance with `--server`, or set a new global default.

```bash
# Issue from Let's Encrypt for this cert only
acme.sh --issue -d example.com -w /var/www/html --server letsencrypt

# Change the default CA for all future issuances
acme.sh --set-default-ca --server letsencrypt
```

| CA | `--server` value | Notes |
|---|---|---|
| ZeroSSL | `zerossl` | Default. May require an EAB-bound account (handled automatically when an email is registered). |
| Let's Encrypt | `letsencrypt` | |
| Let's Encrypt (staging) | `letsencrypt_test` | Testing only — issues untrusted certs. Equivalent to `--staging`. |
| Google Public CA | `google` | Requires EAB credentials. |
| SSL.com | `sslcom` | |
| Actalis | `actalis` | |

For CAs that require **External Account Binding** (EAB), pass the credentials once at registration:

```bash
acme.sh --register-account --server google \
  --eab-kid  <your-key-id> \
  --eab-hmac-key <your-hmac-key>
```

> [!TIP]
> When experimenting, always add `--staging` (or `--server letsencrypt_test`). Production CAs enforce strict rate limits; the staging environment does not, so you can iterate freely without getting blocked.

---

## 6. Installing the Certificate Into Your Server

Issuing a certificate only writes it into `~/.acme.sh/`. The `--install-cert` command copies it to wherever your server expects it and runs a reload command. **This is also what wires up automatic reload-on-renewal** — `acme.sh` remembers these paths and re-runs the reload command every time the cert renews.

```bash
acme.sh --install-cert -d example.com \
  --cert-file      /path/to/cert.pem \
  --key-file       /path/to/key.pem \
  --fullchain-file /path/to/fullchain.pem \
  --reloadcmd      "service nginx force-reload"
```

| Flag | What it receives |
|---|---|
| `--cert-file` | The leaf (domain) certificate only |
| `--key-file` | The private key |
| `--ca-file` | The intermediate (CA) certificate only |
| `--fullchain-file` | Leaf + intermediate concatenated (what most servers want) |
| `--reloadcmd` | Shell command run after install/renew (e.g. reload your web server) |

Only `-d` is required; supply whichever file paths your server needs.

> [!WARNING]
> The `--reloadcmd` is the most important argument here. A certificate can renew perfectly, but without a correct reload command your running server keeps serving the **old** cert from memory until it is restarted. Test your reload command by hand once before trusting it.

### 6.1 Deploy hooks (push to services beyond a local file)

For targets that aren't just "a file on disk + reload" — Docker containers, routers, load balancers, panels, remote hosts over SSH — use a **deploy hook**. `acme.sh` includes **52 deploy hooks**. Each reads `DEPLOY_<NAME>_*` environment variables.

Example: deploy into a running Docker container by label:

```bash
export DEPLOY_DOCKER_CONTAINER_LABEL="sh.acme.autoload.domain=example.com"
export DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"

acme.sh --deploy -d example.com --deploy-hook docker
```

A few common hooks:

| Target | Hook name |
|---|---|
| Remote host over SSH | `ssh` |
| Docker container | `docker` |
| Nginx | `nginx` |
| HAProxy | `haproxy` |
| Synology DSM | `synology_dsm` |
| Proxmox VE | `proxmoxve` |
| HashiCorp Vault | `vault` |

> [!NOTE]
> Like DNS credentials, deploy-hook variables are saved on first use and reused at every renewal, so the deployment repeats automatically. See the [deploy hooks wiki](https://github.com/acmesh-official/acme.sh/wiki/deployhooks) for every hook and its variables.

---

## 7. Renewing Certificates

You normally **never renew manually** — the cron job installed at setup runs `acme.sh --cron` daily and renews anything that is due.

A certificate is renewed when **any** of these is true:

1. You passed `--force`.
2. The CA's **ARI** (ACME Renewal Information, [RFC 9773](https://www.rfc-editor.org/rfc/rfc9773.html)) suggested-renewal window has started.
3. The cached next-renewal time (default: 30 days after issuance) has passed.

ARI support is automatic and needs no flag: when the CA advertises a `renewalInfo` endpoint (Let's Encrypt, ZeroSSL, …), `acme.sh` polls it every cron run and renews early if the CA asks (e.g. during a mass-revocation event).

To force a renewal now:

```bash
acme.sh --renew -d example.com --force
acme.sh --renew -d example.com --force --ecc   # the ECC copy
```

Renew everything at once (this is what cron does):

```bash
acme.sh --renew-all
```

See when a cert is next due:

```bash
acme.sh --info -d example.com | grep Le_NextRenewTimeStr
```

> [!TIP]
> `acme.sh --renew` skips certs that aren't due yet and returns exit code `2` for "skipped". In scripts, treat `2` as success, or pass `--treat-skip-as-success` so it returns `0`.

---

## 8. Renewal Notifications

Get an email or chat message whenever the cron job renews (or fails to renew) a cert. `acme.sh` includes **26 notification hooks** (Slack, Telegram, Discord, Mattermost, Teams, email/SMTP, Pushover, Gotify, and more).

```bash
# Configure a Slack notification
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/XXX/YYY/ZZZ"
acme.sh --set-notify --notify-hook slack
```

A test message is sent immediately to confirm it works. Control *when* you're notified:

```bash
acme.sh --set-notify --notify-level 2   # 0=off 1=errors 2=renewals+errors 3=+skips
acme.sh --set-notify --notify-mode 0    # 0=one bulk message  1=one per cert
```

---

## 9. Managing Certificates

| Task | Command |
|---|---|
| List all certs | `acme.sh --list` |
| List in raw/parseable form | `acme.sh --list --listraw` |
| Show a cert's config | `acme.sh --info -d example.com` |
| Stop renewing a cert | `acme.sh --remove -d example.com` |
| Revoke a cert | `acme.sh --revoke -d example.com` |
| Export to PKCS#12 (`.pfx`) | `acme.sh --to-pkcs12 -d example.com --password mypass` |
| Convert key to PKCS#8 | `acme.sh --to-pkcs8 -d example.com` |

```bash
acme.sh --list
```

```text
Main_Domain  KeyLength  SAN_Domains       CA            Created               Renew
example.com  "ec-256"   www.example.com   ZeroSSL.com   2026-06-04T00:00:00Z  2026-07-04T00:00:00Z
```

> [!NOTE]
> `--remove` stops renewal but leaves the cert and key files on disk. To delete them, remove the `~/.acme.sh/example.com/` directory yourself. `--revoke` tells the CA to invalidate the cert (use it if a key is compromised).

---

## 10. Upgrading acme.sh

```bash
acme.sh --upgrade                    # update to the latest release now
acme.sh --upgrade --auto-upgrade     # enable automatic upgrades during cron
acme.sh --upgrade --auto-upgrade 0   # disable automatic upgrades
```

> [!TIP]
> `acme.sh` evolves quickly and CAs change their requirements over time. Keeping auto-upgrade on is the simplest way to avoid renewal surprises.

---

## 11. Troubleshooting

Run any failing command again with `--debug 2` (or `--debug 3` for maximum detail) to see exactly what `acme.sh` and the CA exchanged.

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

### Error: `Unknown parameter: <flag>`

**Cause:** A flag is misspelled, or belongs to a newer version than you have installed.
**Solution:** Check `acme.sh -h` for the exact spelling, then `acme.sh --upgrade` to get the latest flags.
**Prevention:** Keep auto-upgrade enabled.

### Error: `'dns_<provider>' does not contain 'dns'`

**Cause:** The `--dns` hook name was given without its `dns_` prefix (e.g. `--dns servercow` instead of `--dns dns_servercow`).
**Solution:** Always use the full plugin name, including the prefix: `--dns dns_servercow`.
**Prevention:** Copy the hook name from the [dnsapi wiki](https://github.com/acmesh-official/acme.sh/wiki/dnsapi) or the plugin filename.

### Error: `You didn't specify a Cloudflare api key and email yet.` (or similar per-provider message)

**Cause:** The provider's credential environment variables aren't set in the current shell.
**Solution:** `export` the variables listed in that provider's plugin (e.g. `CF_Token`), then re-run `--issue`. They are saved after the first successful run.
**Prevention:** Set credentials before the first issuance; they persist for renewals automatically.

### Error: `Verify error:Invalid response from http://example.com/.well-known/acme-challenge/...`

**Cause:** The CA could not fetch the challenge file — wrong webroot, the domain doesn't resolve to this server, or a redirect/firewall is interfering.
**Solution:** Confirm `-w` points at the directory actually served at `http://example.com/`, that the domain's DNS A/AAAA record points here, and that port 80 is reachable from the internet.
**Prevention:** Test by manually placing a file in `<webroot>/.well-known/acme-challenge/` and fetching it over HTTP before issuing.

### Error: `Standalone mode failed` / `Could not bind to port 80`

**Cause:** Another process already holds port 80, or you lack permission to bind it.
**Solution:** Stop the conflicting service, run as root, or use `--alpn` (port 443) or `--httpport` to choose a forwarded port.
**Prevention:** Use webroot or DNS mode on servers that already run a web server.

### Error: `It seems that you are using sudo, please read this page first`

**Cause:** `acme.sh` detected it was launched through `sudo` in a way that would store certs under the wrong home directory.
**Solution:** Run as the target user directly (e.g. `su - acmeuser`), or pass `--force` if you intentionally want the current home. See the [sudo wiki](https://github.com/acmesh-official/acme.sh/wiki/sudo).
**Prevention:** Install and run `acme.sh` consistently as one dedicated user.

### Error: certificate renewed but the website still serves the old one

**Cause:** No `--reloadcmd` was set, or it doesn't actually reload the live server.
**Solution:** Re-run `--install-cert` with a correct `--reloadcmd`, then test it by hand.
**Prevention:** Always validate the reload command when you first install the cert.

### Issue: `--list` shows the wrong "Renew" date with short-lived profiles

**Cause:** A known display quirk when using `--cert-profile shortlived`; the stored next-renew time can lag the actual ARI window.
**Solution:** Trust `acme.sh --info -d example.com` (`Le_NextRenewTimeStr`) and the cron behavior over the `--list` column. Upgrade to the latest version.

---

## 12. Frequently Asked Questions

**Q: Do I need to be root?**
No. `acme.sh` installs into your home directory and runs as your user. Root is only needed for standalone mode (binding port 80/443), Apache/Nginx mode, or writing certs into root-owned system paths.

**Q: Where are my certificates stored?**
Under `~/.acme.sh/<main-domain>/`. The files are `<domain>.key` (private key), `<domain>.cer` (leaf cert), `ca.cer` (intermediate), and `fullchain.cer`. Don't point servers at these directly — use `--install-cert`.

**Q: How often do certs renew?**
Every 30 days by default, or earlier if the CA's ARI window opens. The daily cron job handles it automatically.

**Q: Which CA is used by default?**
ZeroSSL. Use `--server letsencrypt` (per cert) or `--set-default-ca --server letsencrypt` to change it.

**Q: Can I get a wildcard certificate?**
Yes — use a DNS-based mode. `acme.sh --issue -d example.com -d '*.example.com' --dns dns_cf`. HTTP challenges cannot validate wildcards.

**Q: ECC or RSA — which should I pick?**
ECC (`ec-256`, the default) is smaller and faster and is supported by all modern clients. Choose RSA (`2048`/`4096`) only if you must support very old clients. You can issue both for the same domain.

**Q: How do I test without hitting rate limits?**
Add `--staging` (or `--server letsencrypt_test`). Staging issues untrusted certs but has generous limits, so you can iterate freely.

**Q: My credentials — do I have to export them every time?**
No. The first successful run saves DNS and deploy-hook variables into `~/.acme.sh/account.conf` (or the per-domain config), and renewals reuse them automatically.

**Q: How do I move acme.sh and my certs to another server?**
Copy the entire `~/.acme.sh/` directory to the new machine and reinstall the cron job with `acme.sh --install-cronjob`. All certs, keys, account data, and saved credentials travel with the directory.

**Q: Does acme.sh modify my Apache/Nginx config?**
No. Apache/Nginx modes only use the server to answer the challenge and restore any temporary changes. You wire the issued cert into your vhost yourself (via `--install-cert`).

**Q: How do I completely uninstall it?**
`acme.sh --uninstall` removes the alias and cron job. Your certs and keys remain in `~/.acme.sh/` — delete that directory by hand if you also want to remove them.

---

_Generated June 2026 from acmesh-official/acme.sh at 3c848ad. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
