Wildcard Certificates + acme.sh: Automating Multi-Subdomain SSL Management
This guide demonstrates using wildcard SSL certificates with acme.sh to automate issuance, renewal, and deployment across multiple subdomains via DNS API validation and fixed Nginx certificate paths.
Problem: Managing Multiple Subdomain Certificates
When running multiple services under subdomains like blog.example.com, www.example.com, api.example.com, and admin.example.com, requesting a separate certificate for each domain creates fragmented maintenance. Each certificate requires individual application, download, placement, configuration, and renewal — with potentially different expiration dates. Over time, certificate management shifts from infrastructure to burden.
Solution: Wildcard Certificate + acme.sh
A wildcard certificate ( *.example.com) covers all first-level subdomains. To also secure the apex domain ( example.com), include both in the request:
-d example.com
-d '*.example.com'This single certificate serves all subdomains, drastically reducing operational overhead.
Wildcard Certificates Require DNS Validation
The CA requires a TXT record like _acme-challenge.example.com to prove DNS control. Manual TXT record creation works for initial issuance but fails for automated renewal. DNS APIs enable automatic record creation and cleanup.
acme.sh: ACME Client for Automation
acme.shis a shell script that supports wildcard certificates, DNS APIs, scheduled renewal, installation to a fixed path, and post-renewal reload commands. The key requirement: certificates must auto-renew and the production Nginx must reload the new certificate.
Cloud Provider Authorization
Create a RAM User with Minimal Permissions
Create a sub-user in the cloud console and grant only one permission : DNS record management. This limits blast radius if the API key leaks.
Create API Key and Secret
Generate an API Key/Secret pair for the RAM user. Store securely (shown only once). Rotate periodically to reduce exposure risk. If the DNS provider supports per-domain or per-record-type authorization, use the narrowest scope.
Installing acme.sh
Official install:
curl https://get.acme.sh | sh -s [email protected]Or via Git:
git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install -m [email protected]For China network issues, see the wiki at https://github.com/acmesh-official/acme.sh/wiki/Install-in-China. Installation creates ~/.acme.sh/ and a cron job. Reload shell and verify with ~/.acme.sh/acme.sh --help.
Configuring DNS API Credentials
Each DNS provider uses different environment variable names (e.g., Alibaba Cloud uses Ali_Key and Ali_Secret). Consult the provider's DNS API documentation. Example for Alibaba Cloud:
export Ali_Key="your-api-key"
export Ali_Secret="your-api-secret"Persist across reboots by appending to ~/.bashrc and sourcing it.
Issuing the Wildcard Certificate
Run the issuance command with your DNS plugin:
acme.sh --issue \
-d example.com \
-d '*.example.com' \
--dns dns_aliReplace dns_ali with your provider's plugin name (e.g., dns_cf for Cloudflare). On first run, acme.sh will:
Connect to the DNS provider via API
Create the TXT validation record
Request the certificate from Let's Encrypt
Delete the TXT record
If issuance fails, diagnose the error (common causes: wrong credentials, insufficient permissions, incorrect plugin name, domain not managed by this DNS provider, TXT propagation delay, CA rate limits) — do not blindly retry.
Deploying the Certificate to Production
Do not point Nginx directly at ~/.acme.sh/ — its internal structure may change. Use --install-cert to copy to a stable, self-managed path. In this setup:
mkdir -p /opt/coduty/nginx/certs/example.com
acme.sh --install-cert -d example.com \
--key-file /opt/coduty/nginx/certs/example.com/privkey.pem \
--fullchain-file /opt/coduty/nginx/certs/example.com/fullchain.pem \
--reloadcmd "docker exec global-nginx nginx -s reload"Critical flags: --fullchain-file (Nginx needs the full chain) and --reloadcmd (ensures the running Nginx loads the renewed certificate). Without reload, the old certificate persists.
Nginx Configuration Example
Each virtual host references the same fixed certificate paths (container-internal paths shown):
server {
listen 443 ssl;
server_name blog.example.com;
ssl_certificate /etc/nginx/certs/example.com/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/example.com/privkey.pem;
location / {
proxy_pass http://blog-app:8000;
}
}Verification
Test Nginx config: docker exec global-nginx nginx -t Reload: docker exec global-nginx nginx -s reload Check cron job: crontab -l (acme.sh installs a daily renewal check)
Manual renewal test: acme.sh --cron or force renew: acme.sh --renew -d example.com --force Verify via browser or CLI:
curl -I https://blog.example.com
openssl s_client -connect blog.example.com:443 -servername blog.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuerTroubleshooting: if apex works but subdomains fail, ensure -d '*.example.com' was included; if subdomains work but apex fails, ensure -d example.com was included.
Summary: Benefits and Risks
Benefits
Multiple subdomains share one certificate
Fixed certificate path
Automated renewal
Adding a new subdomain only requires DNS and Nginx config
Consistent global Nginx container architecture
Risks
Wildcard private key compromise affects all covered subdomains
DNS API credentials must be tightly protected
Automated renewal should be periodically verified reloadcmd must be correct and executable *.example.com does not cover deeper subdomains like
a.b.example.comWildcard certificates solve multi-subdomain coverage, DNS APIs solve automated validation, acme.sh handles issuance, installation, and renewal, and a global Nginx provides unified entry and certificate reference.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code of Duty
"Code of Duty" — Every line of code has its own mission. We avoid shortcuts and quick fixes, focusing on authentic coding reflections and the joys and challenges of technical growth. The journey of learning matters more than any destination. Join us as we humbly forge ahead in the world of code.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
