feat: CERTBOT_DOMAIN may name several domains
Build docker image and push to registry.bitdeals.org / main-build-job (push) Successful in 30s

certbot's own -d already takes a comma-separated list and puts every name
in the certificate's Subject Alternative Names, so `-d "$CERTBOT_DOMAIN"`
needed no change at all. What assumed a single domain was everything that
treated the variable as a *file name*: --cert-name, and the two scripts
that address the lineage under /etc/letsencrypt/live. Certbot names a
lineage after the first domain of the list, so all three now take
${CERTBOT_DOMAIN%%,*} instead of the whole string -- otherwise a two-domain
value asks for a lineage literally called "a.org,b.org" and the renewal
pass looks for a directory nobody made.

One certificate with several names rather than several lineages: HAProxy
binds a single site.pem, and a second lineage would have nowhere to go.

Adding a domain to CERTBOT_DOMAIN on a machine that already holds a
certificate still needs one manual issuance -- `certbot renew` reads the
names off the certificate it has and never looks at the variable. The
README says so, and gives the command.
This commit is contained in:
2026-08-20 11:53:12 +00:00
parent 9dfa45f901
commit 36e1865e37
5 changed files with 20 additions and 9 deletions
+8 -3
View File
@@ -42,7 +42,7 @@ services:
image: registry.bitdeals.org/certbot
restart: unless-stopped
environment:
- CERTBOT_DOMAIN=example.org
- CERTBOT_DOMAIN=example.org,www.example.org
- CERTBOT_EMAIL=admin@example.org # optional, for expiry notices
volumes:
- certificates:/etc/certificates # shared with haproxy
@@ -62,7 +62,7 @@ the one that writes it.
```sh
docker run -d \
-e CERTBOT_DOMAIN=example.org \
-e CERTBOT_DOMAIN=example.org,www.example.org \
-v certificates:/etc/certificates \
-v letsencrypt:/etc/letsencrypt \
-v letsencrypt_work:/var/lib/letsencrypt \
@@ -103,7 +103,7 @@ Container images are configured using parameters passed at runtime.
|Parameter|Function|
|:--------|:-------|
|-e CERTBOT_DOMAIN|The domain to certify. Default: empty — no certificate is requested and the site keeps the self-signed placeholder, silently. One domain only; the scripts pass a single `-d`|
|-e CERTBOT_DOMAIN|The domain to certify, or several comma-separated (`a.org,b.org`) — certbot's own `-d` grammar, giving one certificate that carries every name as a Subject Alternative Name. The first domain names the certificate under `/etc/letsencrypt/live`. Default: empty — no certificate is requested and the site keeps the self-signed placeholder, silently|
|-e CERTBOT_EMAIL|Address for Let's Encrypt expiry notices. Default: empty, which registers with `--register-unsafely-without-email` and leaves you without warnings — see Notes|
|-v /etc/certificates|Shared with HAProxy. Holds `site.pem`: the concatenated certificate and private key HAProxy binds to|
|-v /etc/letsencrypt|Certbot's config directory: the ACME account key, the issued certificates and the renewal configuration. Losing it means re-registering and re-issuing|
@@ -162,6 +162,11 @@ Container images are configured using parameters passed at runtime.
by cron, means a new certbot release reaches the registry — and through
Watchtower, production — without anyone triggering a build. Pin a version tag
for reproducible builds.
- **Adding a domain to `CERTBOT_DOMAIN` does not reissue by itself.** The loop
calls `certbot renew`, which takes the names from the certificate it already
holds and never reads `CERTBOT_DOMAIN`. On a machine that has one, issue the
new set once by hand and the loop keeps it from then on:
`certbot certonly --standalone -n --agree-tos --http-01-port=380 --cert-name <first-domain> --expand -d <the,new,list>`.
- **Let's Encrypt enforces rate limits.** Repeated failed issuance against the
same domain counts against them; test changes against
`--server https://acme-staging-v02.api.letsencrypt.org/directory` before