This guide is for operators, system architects, and developers who deploy acme.sh at scale, integrate it into infrastructure, or want to understand exactly how it works internally. It is the source-based companion to the User Guide, which covers day-to-day certificate operations.
Everything here is derived from the acme.sh source at the commit stamped in the footer (version 3.1.4).
1. Executive Summary
acme.sh is a complete ACME (RFC 8555) certificate-management client implemented entirely in POSIX shell. It has zero compiled dependencies — no Python, Go, Java, or Node runtime — relying only on a shell interpreter, openssl, an HTTP client (curl or wget), and cron. This makes it uniquely portable: it runs on Linux, every major BSD, macOS, Solaris/illumos, Windows (Cygwin), Haiku, and embedded targets like OpenWRT.
| Attribute | Value |
|---|---|
| Language | Unix shell (POSIX sh; auto-upgrades to bash if present) |
| Version | 3.1.4 |
| License | GPLv3 |
| Core engine | Single script acme.sh (~8,660 lines) |
| Extension plugins | 178 DNS providers, 52 deploy hooks, 26 notify hooks |
| Runtime dependencies | openssl, curl/wget, POSIX shell; socat (standalone), cron, logger (syslog), idn (IDN) optional |
| Persistent state | Flat files under ~/.acme.sh/ — no database |
| Default CA | ZeroSSL |
| Default key | ECDSA P-256 (ec-256) |
| Privilege model | Runs unprivileged; root only for privileged modes |
| Supported CAs | ZeroSSL, Let's Encrypt, Google Public CA, SSL.com, Actalis, any RFC 8555 CA |
Architectural philosophy: one self-contained script that "speaks ACME in shell," extended by a flat directory of single-purpose plugin scripts loaded on demand and dispatched purely by naming convention. There is no service, no daemon, and no central state store — every invocation reads flat config files, does its work, and exits. Scheduling is delegated to cron.
2. Architecture
2.1 Component overview
┌──────────────────────────────────────────────┐
│ acme.sh (single script) │
│ │
CLI / cron ─────────▶ │ main() ─▶ _process() (argument parser) │
│ │ │
│ ▼ │
│ command dispatch (case "$_CMD") │
│ issue · renew · deploy · install-cert · … │
│ │ │
│ ┌────────────┼────────────┬──────────────┐ │
│ ▼ ▼ ▼ ▼ │
│ ACME core crypto HTTP client config │
│ (RFC 8555) (openssl (_get/_post (flat- │
│ JWS, nonce, wrappers) via curl/wget) file) │
└───────┬───────────┬───────────┬────────────────┘
│ │ │
loaded on demand │ │ │ loaded on demand
via `. hookfile` ▼ ▼ ▼ via `. hookfile`
┌───────────────┐ ┌───────────┐ ┌──────────────┐
│ dnsapi/ │ │ deploy/ │ │ notify/ │
│ dns_*.sh ×178 │ │ *.sh ×52 │ │ *.sh ×26 │
│ _add / _rm │ │ _deploy │ │ _send │
└───────┬───────┘ └─────┬─────┘ └──────┬───────┘
▼ ▼ ▼
DNS provider target servers chat / email
APIs (SSH, Docker, services
panels, LBs)
Persistent state (flat files): ~/.acme.sh/
account.conf · ca/<host>/<path>/{account.key,account.json,ca.conf}
<domain>/{<domain>.key,.cer,.csr,.conf, ca.cer, fullchain.cer}
2.2 Control flow
The entire program is driven by three top-level functions:
main()(script entry point) — if the first argument starts with-, it calls_process "$@"; otherwise it treats the arguments as a direct internal-function call (this is how plugins and the cron wrapper re-enter the script)._process()— a single largewhile/caseloop that parses every CLI flag into shell variables (_domain,_webroot,_keylength, …), then a secondcase "$_CMD"block dispatches to the command function with the collected arguments.- Command functions —
issue,renew,renewAll,deploy,installcert,revoke,install,cron, etc. These call the shared ACME-core and crypto helpers.
There are no subcommands in the git/docker sense; the interface is entirely flag-driven. The command verbs (--issue, --renew, …) are mutually-exclusive flags that set _CMD.
2.3 The plugin model (the key extensibility pattern)
All three plugin families follow an identical contract: a flat directory of single-file shell scripts, each sourced on demand and invoked through a naming convention rather than any registration step.
| Family | Directory | Sourced when | Required function(s) | Invocation |
|---|---|---|---|---|
| DNS API | dnsapi/ |
--dns dns_<p> |
dns_<p>_add, dns_<p>_rm |
Add/remove _acme-challenge TXT records |
| Deploy | deploy/ |
--deploy-hook <n> |
<n>_deploy |
Push cert to a target |
| Notify | notify/ |
--notify-hook <n> |
<n>_send |
Send a renewal/error message |
When a hook runs, the core does . "$hook_file" to load it into the current shell, checks that the expected function exists (_exists "$d_command"), and calls it. A failure to find the function produces an explicit error: "Make sure it has a function named: …". This convention-over-configuration design is why adding a new provider is just dropping a correctly-named file into the directory — no central manifest to edit.
Each DNS plugin also carries a self-documenting metadata block, e.g. Cloudflare's:
dns_cf_info='CloudFlare
Site: CloudFlare.com
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_cf
Options:
CF_Key API Key
CF_Email Your account email
OptionsAlt:
CF_Token API Token
CF_Account_ID Account ID
CF_Zone_ID Zone ID. Optional.
'
This dns_<p>_info string is a machine-readable manifest of the variables each plugin reads — the authoritative source for "what credentials does provider X need."
2.4 The ACME protocol core
The core implements RFC 8555 directly in shell: it fetches the CA's directory resource, manages replay nonces, builds and signs JWS payloads, places new orders, answers challenges (HTTP-01, DNS-01, TLS-ALPN-01, or the draft DNS-persist-01), finalizes with a CSR, and downloads the issued chain. Renewal additionally implements ARI (RFC 9773) — see §7.
DNS propagation is verified before asking the CA to validate, using DNS-over-HTTPS so the check works regardless of the local resolver. Four DoH providers are built in (Cloudflare, Google, Alibaba, DNSPod), selectable via the DOH variables.
3. Cryptography
acme.sh performs no cryptography itself — every operation shells out to the openssl binary (overridable via --openssl-bin / ACME_OPENSSL_BIN). The script wraps openssl for:
| Operation | Helper | Notes |
|---|---|---|
| Key generation | _createkey |
EC (prime256v1/secp384r1/secp521r1) via ecparam -genkey; RSA via genrsa (uses -traditional when available) |
| CSR creation | _createcsr |
Builds an openssl config with SAN, extended key usage, optional OCSP-must-staple and the ACME validation extension |
| Signing (JWS) | _sign |
RSA → dgst -sign; EC → DER signature parsed via asn1parse and re-encoded to the raw r‖s form ACME requires, zero-padded to the curve length |
| Digests | _digest |
sha256/sha1/md5/sha3-256, base64 or hex output |
| HMAC | _hmac |
For External Account Binding; falls back across two openssl invocation styles |
| Fingerprints | _fingerprint |
Strips openssl's =/: formatting |
Because it cannot assume coreutils are present, the script reimplements base64, hex dump, hex↔binary, and URL-encoding in pure shell with multiple fallbacks (xxd → hexdump → od → an ASCII loop; printf '\xNN' support is probed at startup because some shells don't support it). This is the price of true portability.
Note
Keys never leave the host. Account keys live under the per-CA directory; domain keys under each domain directory.
acme.shis a pure client — there is no phone-home telemetry and no key escrow.
4. Configuration Reference
4.1 Directory layout and home variables
~/.acme.sh/ ← LE_WORKING_DIR (the script + plugins live here)
├── acme.sh ← the installed copy
├── account.conf ← global config (sourced as shell)
├── dnsapi/ deploy/ notify/ ← installed plugin copies
├── ca/<acme-host>/<path>/ ← per-CA account data
│ ├── account.key ← ACME account private key
│ ├── account.json ← registered account object
│ └── ca.conf ← per-CA config (e.g. DEFAULT_PREFERRED_CHAIN)
└── <domain>/ ← per-certificate directory (cert NAME)
├── <domain>.key ← private key
├── <domain>.csr <domain>.csr.conf
├── <domain>.cer ← leaf certificate
├── ca.cer ← intermediate
├── fullchain.cer ← leaf + intermediate
├── <domain>.conf ← per-domain renewal config (Le_* vars)
└── backup/ ← pre-renewal backups of installed files
ECC certificates for the same domain are stored in a parallel <domain>_ecc/ directory (the _ecc suffix), so RSA and ECC certs coexist without collision.
Key location variables (set via flags or environment, persisted in account.conf):
| Variable | Flag | Default | Purpose |
|---|---|---|---|
LE_WORKING_DIR |
--home |
$HOME/.acme.sh |
Where the script and plugins live |
LE_CONFIG_HOME |
--config-home |
= LE_WORKING_DIR |
Where certs/config are written (separate it to keep config out of the code dir) |
CERT_HOME |
--cert-home |
= LE_CONFIG_HOME |
Root for per-domain cert directories |
ACCOUNT_CONF_PATH |
--accountconf |
<config-home>/account.conf |
Global config file |
CA_HOME |
— | <config-home>/ca |
Per-CA account data root |
Tip
For multi-tenant or packaged deployments, set
--config-hometo a writable data volume distinct from the (read-only) code directory. The Docker image and several distro packages rely on exactly this separation.
4.2 Configuration layering
Config is layered, each a plain shell file sourced in order so later layers override earlier ones:
account.conf (global) → ca/<host>/<path>/ca.conf (per-CA) → <domain>/<domain>.conf (per-cert)
Values are written by _saveaccountconf / _savedomainconf / _savecaconf and read back by the matching _read*conf helpers. Per-domain renewal settings are stored as Le_* variables (e.g. Le_Domain, Le_Alt, Le_Webroot, Le_Keylength, Le_RenewalDays, Le_NextRenewTime, Le_API).
Five sensitive/multiline values are base64-encoded in the config files to survive shell sourcing safely:
Le_PreHook Le_PostHook Le_RenewHook Le_Preferred_Chain Le_ReloadCmd
4.3 Notable account-level settings
| Variable | Set via | Effect |
|---|---|---|
DEFAULT_ACME_SERVER |
--set-default-ca --server <ca> |
Global default CA |
USER_AGENT |
--useragent |
HTTP User-Agent (saved for reuse) |
ACME_OPENSSL_BIN |
--openssl-bin |
Path to a custom openssl |
AUTO_UPGRADE |
--upgrade --auto-upgrade |
Self-upgrade during cron |
LOG_FILE / LOG_LEVEL |
--log / --log-level |
File logging |
SYS_LOG |
`--syslog <0 | 3 |
NOTIFY_HOOK / NOTIFY_LEVEL / NOTIFY_MODE / NOTIFY_SOURCE |
--set-notify … |
Renewal notifications |
ACME_USE_IPV4_REQUESTS / ACME_USE_IPV6_REQUESTS |
--request-v4 / --request-v6 |
Force IP family for CA connections |
ACME_USE_WGET |
--use-wget |
Force wget even if curl is present |
5. Deployment Patterns
5.1 Single host with a local web server (most common)
Install as the service user, issue via webroot or the Apache/Nginx mode, and install the cert with a reload command. The cron job handles everything thereafter. This is the canonical pattern in the User Guide §1.
5.2 Centralized issuance + push deployment
Issue certificates on a dedicated "cert host" (often using DNS API mode so no inbound HTTP is required), then use deploy hooks to push each renewed cert to the machines that serve it — over SSH, into Docker containers, to load balancers, or to device/panel APIs. Deploy-hook variables (DEPLOY_<NAME>_*) are saved per-domain on first run and replayed automatically at every renewal.
# On the cert host: issue via DNS API, then deploy to a remote node over SSH
export CF_Token=...; export CF_Account_ID=...
acme.sh --issue --dns dns_cf -d app.example.com
export DEPLOY_SSH_USER=deploy
export DEPLOY_SSH_SERVER=node1.internal
export DEPLOY_SSH_KEYFILE=/etc/ssl/app.key
export DEPLOY_SSH_FULLCHAIN_FILE=/etc/ssl/app.cer
export DEPLOY_SSH_RELOAD_CMD="systemctl reload nginx"
acme.sh --deploy -d app.example.com --deploy-hook ssh
5.3 Container deployments
The official neilpang/acme.sh Docker image runs the client with ~/.acme.sh on a mounted volume. For sidecar patterns that issue on one container and serve on another, the docker deploy hook copies certs into a target container (matched by label) and runs a reload command inside it.
Warning
A recurring operational pitfall (tracked in issue #6902) is that renewal succeeds but the deploy step never re-runs, leaving the serving container on the old cert. Deploy hooks only replay automatically if they were saved during a prior
--deployrun. Verify after the first renewal that the target actually received the new cert (e.g.echo | openssl s_client -connect host:443 2>/dev/null | openssl x509 -noout -dates).
5.4 Stateless / DNS-persist for fleets behind proxies
When many front-ends share a domain and you cannot guarantee which one answers an HTTP challenge, use stateless mode (a known key authorization served by all nodes) or DNS-persist mode (a single long-lived TXT record reused for every renewal). Both avoid per-node challenge plumbing.
6. Scheduling & the Cron Pipeline
The installer adds a daily entry:
0 0 * * * "/home/user/.acme.sh"/acme.sh --cron --home "/home/user/.acme.sh" > /dev/null
--cron sets _ACME_IN_CRON=1, optionally self-upgrades (if AUTO_UPGRADE=1), then runs renewAll with skip-as-success semantics. Manage the cron job independently of installation:
acme.sh --install-cronjob # (re)install the cron entry
acme.sh --uninstall-cronjob # remove it
On Windows (Cygwin), scheduling uses the Task Scheduler via schtasks instead of cron; the task is named acme.sh.cron.
Note
Because
cronruns with a minimal environment,acme.shre-sourcesaccount.confand exportsUSER_PATHto restore the PATH. This is why saved credentials (rather than shell-exported ones) are essential for unattended renewal.
7. Renewal Logic (ARI / RFC 9773)
A certificate is renewed when any condition holds:
--forceis supplied.- The CA's ARI
suggestedWindowhas started. - The cached
Le_NextRenewTimehas passed (default fallback:DEFAULT_RENEW=30days).
When the CA's directory advertises a renewalInfo endpoint, acme.sh follows ARI automatically — no opt-in:
| Behavior | When | Why |
|---|---|---|
Polls suggestedWindow |
Every cron run, before deciding to skip | Lets the CA pull renewals forward during incidents (mass revocation, key compromise) |
| Picks a random time inside the window | After a successful issuance/renewal | Spreads load so clients don't stampede the CA |
Sends replaces=<certID> in newOrder |
On renewal | Correlates the new order with the cert it supersedes (RFC 9773 §5) |
Retries without replaces |
On alreadyReplaced / ARI validation error |
Robustness across CA switches and retired issuers |
Inspect the computed next-renewal time:
acme.sh --info -d example.com # Le_NextRenewTimeStr=...
8. Security Analysis
8.1 Privilege model
acme.sh is designed to run unprivileged. The working directory is created chmod 700 and account.conf chmod 600; private keys are written with chmod 600. Root is required only to bind ports 80/443 (standalone), drive Apache/Nginx, or write certs into root-owned paths. A sudo guard (_checkSudo) refuses to run under a misleading sudo context unless --force is given, to prevent certs landing in the wrong home directory.
8.2 Secret handling
- Credentials at rest: DNS/deploy/notify credentials are stored in plaintext inside
account.conf(or per-domain config), protected only by file permissions (600). Anyone who can read these files gets the API tokens. Treat the config home as sensitive; encrypt the volume or restrict the user as appropriate. - Secrets in logs: sensitive values are hidden by default in output, debug, and log streams — they render as
[hidden](please add '--output-insecure' to see this value)via the_secure_debug*helpers.--output-insecure(orOUTPUT_INSECURE=1) reveals them; use it only for short-lived debugging.
Caution
Never paste
--debug 2/--debug 3output that was captured with--output-insecureinto a public issue — it can contain account keys, DNS API tokens, and EAB HMAC secrets in the clear.
8.3 Code-execution surface
- Hooks are sourced shell. DNS, deploy, and notify plugins are loaded with
.(source) into the running shell and executed. They run with the invoking user's privileges. Only install plugin files from sources you trust; a malicious file dropped intodnsapi//deploy//notify/runs as you. evalon user commands.--reloadcmd,--pre-hook,--post-hook, and--renew-hookvalues areeval'd. They originate from your own config (base64-stored), so the risk is realized only if an attacker can write to your config files — another reason to lock down the config home.- Self-upgrade.
--upgrade(and auto-upgrade during cron) downloads a tarball from GitHub over HTTPS and replaces the installed script. AnUPGRADE_HASHis recorded to skip no-op upgrades. This is convenient but means an enabled auto-upgrade trusts the upstream release channel; in high-assurance environments, pin a version and upgrade deliberately.
8.4 Transport security
CA and DNS API calls use HTTPS via curl/wget. --insecure disables peer verification (for devices with untrusted API certs) and --ca-bundle/--ca-path point at a custom trust store. Avoid --insecure for CA communication.
8.5 Hardening checklist
- [ ] Run
acme.shas a dedicated, unprivileged user. - [ ] Place
--config-homeon a volume with restricted permissions; consider encryption at rest. - [ ] Scope DNS API tokens to the single zone(s) you issue for (e.g. a Cloudflare token limited to
DNS:Editon one zone). - [ ] Pin a version and upgrade deliberately in regulated environments; otherwise keep auto-upgrade on for CA-compatibility.
- [ ] Never run
--output-insecureoutside of controlled debugging; scrub any logs before sharing. - [ ] Audit the contents of
dnsapi/,deploy/, andnotify/after any manual plugin install. - [ ] Verify deploy actually delivered the cert after the first automated renewal.
9. Operations Runbook
| Task | Command |
|---|---|
| Health: list certs and renewal dates | acme.sh --list |
| Inspect one cert's config | acme.sh --info -d example.com |
| Force a renewal (debugging) | acme.sh --renew -d example.com --force [--ecc] |
| Renew everything (manual cron) | acme.sh --renew-all |
| Verify served cert vs issued cert | echo | openssl s_client -connect host:443 2>/dev/null | openssl x509 -noout -dates -subject |
| Reissue with a new key on renewal | add --always-force-new-domain-key |
| Stop renewing | acme.sh --remove -d example.com |
| Revoke (key compromise) | acme.sh --revoke -d example.com --revoke-reason 1 |
| Move install | copy the whole ~/.acme.sh/, then --install-cronjob |
Backups: the single source of truth is the config home (~/.acme.sh/ by default). Backing up that one directory captures account keys, domain keys, certs, renewal config, and saved credentials. Restore = copy it back and reinstall the cron job.
Logging: enable persistent logs with --log (and --log-level 2) or route to syslog with --syslog 6. Without these, output goes only to stdout/stderr (and > /dev/null in the default cron line, so failures are silent unless you configure notifications).
Tip
Configure notifications (§8 of the User Guide) on any unattended host. The default cron entry discards output, so a notify hook is your only signal that a renewal failed.
10. Repository Structure (for contributors)
acme.sh ← the entire engine (one ~8,660-line script)
dnsapi/ ← 178 DNS provider plugins (dns_<p>.sh)
README.md ← contributor notes for DNS plugins
deploy/ ← 52 deployment hooks (<name>.sh)
README.md ← contributor notes for deploy hooks
notify/ ← 26 notification hooks (<name>.sh)
Dockerfile ← official image build
.github/workflows/ ← CI: ShellCheck, Pebble strict ACME tests, per-OS matrix
10.1 Authoring a plugin
To add a DNS provider, create dnsapi/dns_<provider>.sh exposing:
dns_<provider>_info='Provider Name
Site: provider.example
Docs: github.com/acmesh-official/acme.sh/wiki/dnsapi#dns_<provider>
Options:
PROVIDER_Api_Key API key
'
dns_<provider>_add() { # $1=full record name, $2=TXT value
fulldomain="$1"; txtvalue="$2"
# read creds (env first, saved config fallback), create the TXT record
}
dns_<provider>_rm() { # cleanup after validation
fulldomain="$1"; txtvalue="$2"
}
Read credentials with the env-first / saved-config-fallback idiom (${VAR:-$(_readaccountconf_mutable VAR)}) and persist them with _saveaccountconf_mutable so renewals work unattended. Deploy hooks implement <name>_deploy and use _getdeployconf/_savedeployconf; notify hooks implement <name>_send.
10.2 Quality conventions
The codebase is checked by ShellCheck and validated end-to-end against the Pebble strict ACME test server in CI, across a large OS matrix. Plugins must be POSIX-portable (no bashisms), since the engine targets dash/ash as well as bash.
11. Code Quality & Known Constraints
Strengths
- Exceptional portability: extensive runtime feature-detection (tr variants,
sed -iflavors,egrep,printf '\x'support, Solaris quirks) lets one codebase run nearly everywhere. - Defense-in-depth on secrets in output (
_secure_debugfamily) and conservative file permissions. - A clean, uniform plugin contract that has scaled to 256 community plugins without central coordination.
- Direct RFC 8555 + RFC 9773 implementation kept current with CA behavior.
Structural constraints
- Monolithic engine. The ~8,660-line single file concentrates all core logic. It is comprehensible but hard to unit-test in isolation; correctness leans heavily on the CI integration suite (ShellCheck + Pebble) rather than fine-grained tests.
- Shell-reimplemented primitives. base64/hex/URL-encode are hand-rolled with multiple fallbacks for portability — robust but a maintenance and audit surface that a compiled client wouldn't have.
- Plugin maintenance load. With 178 DNS providers, individual provider APIs drift and break; a steady stream of issues tracks per-provider breakage (e.g. CDMon, infomaniak, IP-Projects, servercow in the current issue list). Each plugin is community-maintained and only as reliable as its upstream API.
12. Improvement Opportunities
These observations come from reading the source and the current open-issue set; they are suggestions for maintainers and downstream packagers, not defects in normal operation.
-
Deploy-after-renew reliability (#6902). Make the renew→deploy linkage more discoverable and add a post-renewal verification/notification path so a silently-skipped deploy is surfaced rather than discovered when a cert expires on the edge.
-
Per-domain config completeness for dual RSA+ECC (#6986).
Le_DNSSleep(and potentially other per-issuance settings) can be missing from the second key-type's<domain>.confwhen issuing both RSA and ECC for the same names. Persist per-issuance settings symmetrically across both key directories. -
Validation-mode input validation. Mistyped modes surface as generic
Unknown parameter/Supported validation types are…errors (#6962, #6956). Earlier, mode-specific validation with a hint ("did you mean--dns dns_<x>?") would cut a class of support issues. -
Secret storage option. Plaintext credentials in
account.confare acceptable with strict file perms but block some compliance regimes. An optional pluggable secret backend (e.g. read tokens from a keyring/agent at runtime instead of persisting them) would broaden enterprise fit. -
Testability of the monolith. Extracting pure helper groups (encoding, config I/O, CSR building) into separately
source-able units with focused tests would reduce regression risk as the single file grows, without changing the shipped single-file ergonomics. -
IPv6 reach for select integrations (#6993, #6872). Some endpoints (e.g. a Route 53 path, ZeroSSL default-CA) are reported IPv4-only; surfacing the chosen address family in
--debugoutput helps operators on IPv6-only networks diagnose quickly.
13. Quick Reference
Commands
| Verb | Action |
|---|---|
--install / --uninstall / --upgrade |
Manage the acme.sh install |
--issue |
Issue a certificate |
--renew / --renew-all |
Renew one / all certs |
--install-cert / -i |
Copy cert to server + set reload |
--deploy |
Run a deploy hook |
--revoke / --remove |
Revoke / stop tracking a cert |
--list / --info |
Inspect certs |
--register-account / --update-account / --deactivate-account |
Account ops |
--set-default-ca / --set-default-chain |
CA defaults |
--set-notify |
Configure notifications |
--cron / --install-cronjob / --uninstall-cronjob |
Scheduling |
--to-pkcs12 / --to-pkcs8 / --create-csr / --sign-csr |
Format / CSR utilities |
--make-dns-persist-value |
Print a DNS-persist TXT record |
High-value parameters
| Flag | Purpose |
|---|---|
-d <domain> |
Domain (repeat for SAN; first is the cert name) |
-w <dir> / --standalone / --alpn / --apache / --nginx / --dns <hook> / --stateless / --dns-persist |
Validation mode |
-k <len> |
Key type: ec-256/ec-384/ec-521/2048/3072/4096/8192 |
--server <ca> / --staging |
Select CA / use staging |
--cert-file / --key-file / --ca-file / --fullchain-file / --reloadcmd |
Install targets |
--days <n> |
Renewal window (default 30) |
--force |
Force action |
--debug [0-3] / --log [file] / --output-insecure |
Diagnostics |
--eab-kid / --eab-hmac-key |
External Account Binding |
--home / --config-home / --cert-home |
Relocate working/config/cert dirs |
Default file paths (per domain, under ~/.acme.sh/<domain>/)
<domain>.key · <domain>.cer · ca.cer · fullchain.cer · <domain>.csr · <domain>.conf