Add one CNAME record, then AutoSSL handles certificate issuance and renewal.
Set up one domain, such as alias.com, on Cloudflare and give its API token to AutoSSL. Any other domain can point a CNAME record to a validation record under alias.com, even when it uses a different DNS provider. AutoSSL then uses acme.sh DNS alias mode to issue and renew its certificate without needing that domain's DNS API credentials.
The certificate is still issued to the target domain, not alias.com. AutoSSL requests one ECDSA P-256 certificate containing both the base domain and its wildcard, then provides stable certificate and private-key URLs for deployment scripts.
| Direct DNS API issuance | AutoSSL |
|---|---|
| Store API credentials for every target DNS zone. | Store one Cloudflare token for the validation zone. |
| Depend on each target DNS provider's API. | The target provider only needs to support CNAME records. |
| Run issuance, renewal, and deployment on every server. | Issue and renew centrally; target servers only download files. |
The target domain does not need to use Cloudflare, expose its DNS API, or move its nameservers. The CNAME is configured once and reused for automatic renewal.
Assume alias.com is the validation domain hosted on Cloudflare and example.com needs a certificate.
-
Set
ACME_ALIAS=alias.comandACME_ALIAS_PER_DOMAIN=truein AutoSSL. -
Add this record to the DNS zone for
example.com:_acme-challenge.example.com. CNAME _acme-challenge.example.com.alias.com. -
Submit the base domain
example.comin the AutoSSL console. -
AutoSSL runs acme.sh with the Cloudflare DNS hook:
acme.sh --issue --dns dns_cf \ -d example.com -d '*.example.com' \ --challenge-alias example.com.alias.com \ --challenge-alias example.com.alias.com \ --keylength ec-256 -
acme.sh uses
CF_Tokento write the TXT challenge under the Cloudflare zone. Let's Encrypt queries the target domain and follows the CNAME:_acme-challenge.example.com CNAME -> _acme-challenge.example.com.alias.com TXT <- written by acme.sh through the Cloudflare API -
Let's Encrypt validates the TXT record and issues a certificate for
example.comand*.example.com. AutoSSL installs the full chain and private key under stable download URLs.
AutoSSL runs acme.sh --cron every day at 01:30 container time. Keep the CNAME records and persist /root/.acme.sh so renewals continue to use the same validation path and ACME account.
Add a domain such as alias.com to Cloudflare and use it as the dedicated DNS-01 validation zone. Create an API token scoped to this zone with DNS edit and zone read permissions, then record its Zone ID.
The token only needs access to alias.com; it does not need access to any target domain.
For every target base domain, add the CNAME shown above. If the target domain also uses Cloudflare, set the record to DNS only. Do not remove the record after issuance because renewal reuses it.
Create docker-compose.yml:
services:
autossl:
image: ghcr.io/pupilcc/autossl:latest
container_name: autossl
restart: always
ports:
- "3000:3000"
volumes:
- data:/root/data
- acme:/root/.acme.sh
environment:
DOMAIN: https://ssl.example.com
ADMIN_USERNAME: admin
ADMIN_PASSWORD: replace-with-a-long-random-password
DOWNLOAD_AUTH_ENABLED: "true"
DOWNLOAD_AUTH_TOKEN: replace-with-a-long-random-token
ACME_CA: letsencrypt
ACME_EMAIL: admin@example.com
ACME_DNS: dns_cf
ACME_ALIAS: alias.com
ACME_ALIAS_PER_DOMAIN: "true"
ACME_DEBUG: "false"
CF_Zone_ID: xxxxxxxx
CF_Token: xxxxxx
volumes:
data:
acme:Start the service:
docker compose up -dConfigure the HTTPS reverse proxy so ssl.example.com reaches the web console on port 3000. The Go API listens only on 127.0.0.1:1323 inside the container and must not be exposed directly. Then sign in and submit only the base domain, such as example.com; AutoSSL adds the wildcard automatically.
| Variable | Purpose |
|---|---|
DOMAIN |
Public console URL used to restrict administrative action origins and build /dl/... download links; omit the trailing slash. |
ADMIN_USERNAME |
Console username. |
ADMIN_PASSWORD |
Console password and JWT signing secret; use a long random value. |
DOWNLOAD_AUTH_ENABLED |
Only true enables download authentication; unset or any other value preserves unauthenticated downloads. |
DOWNLOAD_AUTH_TOKEN |
Download authentication token; required when download authentication is enabled. |
ACME_CA |
acme.sh CA name, such as letsencrypt. |
ACME_EMAIL |
ACME account email. |
ACME_DNS |
acme.sh DNS hook; use dns_cf for the Cloudflare validation zone. |
ACME_ALIAS |
The single Cloudflare-managed validation domain. |
ACME_ALIAS_PER_DOMAIN |
Only true uses <target-base-domain>.<ACME_ALIAS>; every other value makes all domains share ACME_ALIAS. See below. |
ACME_DEBUG |
true enables acme.sh debug level 1. |
CF_Zone_ID |
Zone ID of the Cloudflare validation domain. |
CF_Token |
API token allowed to manage DNS records in that zone. |
This variable controls whether AutoSSL creates a separate validation alias for each target domain or sends every target directly to ACME_ALIAS. It is enabled only when its value is true (case-insensitive); an unset value, false, or any other value disables it.
With ACME_ALIAS=alias.com and a certificate target of example.com:
| Setting | Validation alias passed to acme.sh | CNAME target | Result |
|---|---|---|---|
ACME_ALIAS_PER_DOMAIN=true |
example.com.alias.com |
_acme-challenge.example.com.alias.com |
Each target base domain uses an isolated record. Recommended. |
Unset or not true |
alias.com |
_acme-challenge.alias.com |
All target domains share one record, so concurrent validations can interfere. |
When enabled, the alias is built as <target-base-domain>.<ACME_ALIAS>. AutoSSL removes a trailing dot and a leading *. from the input and converts it to lowercase; for example, *.Example.COM. becomes example.com.alias.com. The example.com and *.example.com names on the same certificate deliberately share this alias, while other certificate targets get their own aliases. They can therefore share one Cloudflare zone and API token without sharing challenge records.
Each target domain's CNAME must match the selected mode. This variable affects the arguments generated when AutoSSL starts a new acme.sh --issue; acme.sh --cron uses its saved issuance configuration. When changing ACME_ALIAS or this variable, keep the old CNAME until the affected certificate has been reissued with the new configuration and its renewal path has been confirmed.
If a target domain also uses Cloudflare, Universal SSL can publish hidden _acme-challenge TXT records in the target zone. Let's Encrypt may then reject the delegated challenge with Incorrect TXT record.
If this occurs, disable Universal SSL under SSL/TLS -> Edge Certificates for the target domain's zone, wait for DNS caches to expire, and retry. This affects proxied sites that rely on Cloudflare edge certificates, so confirm they have another valid edge certificate first. See Cloudflare's explanation.
The console provides separate .crt and .key URLs for every certificate. Each download code can be rotated independently, and rotation immediately invalidates the corresponding old URL. Private-key responses include Cache-Control: no-store; still treat private-key URLs as secrets and expose AutoSSL only over HTTPS.
DOWNLOAD_AUTH_ENABLED is configured only on the AutoSSL server. The distribution script on target servers does not read this switch.
| AutoSSL server configuration | Download requirement |
|---|---|
DOWNLOAD_AUTH_ENABLED=false or unset |
No token is required. |
DOWNLOAD_AUTH_ENABLED=true |
DOWNLOAD_AUTH_TOKEN is required, and requests must include Authorization: Bearer <DOWNLOAD_AUTH_TOKEN>. A missing or incorrect token returns 401 Unauthorized. |
Send the token in the request header, not in the download URL.
Use scripts/distribute-certificates.sh on each target server:
- Replace the placeholder URLs and destination paths in
urls_and_paths. - Keep the destination filenames
fullchain.pemandprivkey.pem. - Run the script as root according to the AutoSSL server's download authentication setting:
| AutoSSL server configuration | Command |
|---|---|
| Authentication disabled | sudo scripts/distribute-certificates.sh |
| Authentication enabled | sudo env DOWNLOAD_AUTH_TOKEN=your-token scripts/distribute-certificates.sh |
The script downloads files atomically, validates them with OpenSSL when available, fixes permissions, and reloads Nginx only when a file changed and nginx -t succeeds.
