Certificates for devices behind no proxy
Some things on a network need a valid certificate and cannot sit behind a reverse proxy — a NAS, a firewall, a router, a mail server. This stack issues and renews certificates for them and writes the files somewhere you can upload from. One container.
Last run on a host 8 Nov 2026
Pinned at 0.2.1 (released 4 May 2026) · the newest upstream release
In the upstream images: 3 CRITICAL · 46 HIGH with a published fix — per image
Needs: a domain whose DNS is at Cloudflare, and a scoped API token for it.
Installation
Section titled “Installation”From core/acme-certs/:
-
Copy the environment file and set
ACME_EMAILandCERT_DOMAIN:Terminal window cp .env.example .env -
Create the Cloudflare API token as a secret. It needs Zone → DNS → Edit on the target zone — a scoped token, not the global API key:
Terminal window mkdir -p .secretsprintf '%s' 'your-cloudflare-api-token' > .secrets/cf_token.txt -
Start the container. It runs a scheduler in the background and issues nothing on its own:
Terminal window docker compose up -d -
Issue the first certificate:
Terminal window ./scripts/wizard.sh nas.example.comThe wizard asks for the key type and the certificate authority, then writes the files. For a wildcard, add
-w.
Verify
Section titled “Verify”ls volumes/output/nas.example.com/Four files: cert.pem, fullchain.pem, privkey.pem and ca.pem. Upload one
of them to a device and load the page — a certificate that exists on disk and one
a browser accepts are different claims.
Using the files
Section titled “Using the files”Most devices want fullchain.pem and privkey.pem. These four ask for
something else:
| Device | Upload |
|---|---|
| Synology DSM | cert.pem, privkey.pem, ca.pem as the intermediate |
| OPNsense, pfSense | cert.pem, privkey.pem, ca.pem |
| Nginx Proxy Manager | fullchain.pem and privkey.pem, ca.pem optional |
| Windows, IIS, .NET | a PFX bundle — see below |
Windows and IIS do not read PEM. Convert with a password you choose, and enter the same one when importing:
docker compose exec acme-certs \ /scripts/convert-to-pfx.sh example.com your-chosen-passwordGoing further
Section titled “Going further”Renewal
Section titled “Renewal”The container renews on its own through a scheduler that runs inside it. What it
does not do is upload anything — a renewed certificate lands in
volumes/output/ and every device still points at the copy you gave it. Plan for
that: either a script that pushes the new file, or a calendar entry.
To renew by hand:
docker compose exec -e CERT_DOMAIN=example.com acme-certs /scripts/renew.shIssuing without the wizard
Section titled “Issuing without the wizard”For scripting:
docker compose exec \ -e CERT_DOMAIN=example.com \ -e CERT_SAN='*.example.com' \ acme-certs /scripts/issue.shCertificate authority
Section titled “Certificate authority”ACME_SERVER in .env selects it: Let’s Encrypt by default, with ZeroSSL and
Buypass as alternatives. ZeroSSL registers the address in ACME_EMAIL first.
Backup
Section titled “Backup”| State | volumes/acme/ — the acme.sh account key and the issued certificate store |
| Also needed | .secrets/cf_token.txt |
| Reproducible | volumes/output/ — rendered from volumes/acme/ on the next issue or renewal |
| Database | None. |
The account key in volumes/acme/ is the part that matters. Certificates can
always be reissued; a lost account key means registering again with the CA and
losing the issuance history and rate-limit standing tied to it. It is small and
almost never changes, which makes it easy to leave out of a backup.
The private keys in both folders are what a device is trusting: anyone who reads them can impersonate the device until the certificates expire.
Restore order: restore volumes/acme/ and the token, then let the tool
regenerate volumes/output/ rather than restoring it.
Updates
Section titled “Updates”The image is this project’s own build of acme.sh; there is no third-party release feed for it.
# Bump APP_TAG in .env, then:docker compose pulldocker compose up -ddocker compose exec acme-certs acme.sh --versionChanges in the bundled client are listed in the acme.sh releases.
Troubleshooting
Section titled “Troubleshooting”Issuing fails at the DNS challenge. The token is missing Zone → DNS → Edit
on that zone, or it is a global key rather than a scoped token.
The device rejects the file. Check the table above — several want cert.pem
with a separate CA rather than fullchain.pem.
A device shows an expired certificate although renewal ran. Renewal writes to
volumes/output/ and does not upload. The device still holds the old copy.
Repository files
Section titled “Repository files”core/acme-certs/README.md— setup, per-device import steps, script referencecore/acme-certs/UPSTREAM.md— source and upgrade checklist
Official sources
Section titled “Official sources”© 2026 SecDockBlueCode Apache-2.0Site content CC BY-NC 4.0