📦 Certbot Domain Bundler
Certbot Domain Bundler automates the creation of Let's Encrypt certificates for multiple domains using SANs (Subject Alternative Names).
It intelligently groups domains and supports both DNS and webroot challenges.
🧭 How it works
flowchart TD
A["certbundle --domains a.example.com,b.example.com,..."] --> B["hash = first 8 hex of sha256 over the sorted domain list"]
A --> C{"--domain-categories given?"}
C -- yes --> D["group by the longest matching category"]
C -- no --> E["group by base domain SLD.TLD"]
D --> F{"group larger than --chunk-size?"}
E --> F
F -- yes --> G["split the group into batches"]
F -- no --> H["one batch per group"]
G --> I
H --> I["cert name = certbundle-HASH-NNNNN"]
B --> I
I --> J["certbot certonly, webroot or dns-PLUGIN"]
J --> K["classify: updated / skipped / failed"]
K --> L{"more batches?"}
L -- yes --> I
L -- no --> M["print the summary"]
M --> N{"any batch failed?"}
N -- yes --> O["exit 1"]
N -- no --> P["exit 0"]
The batch counter runs across all groups, so the numbering is consecutive over the whole run. A failing batch never aborts the remaining ones.
🚀 Installation
pip install certbundle
pip is the single supported installation path.
After installation, usage instructions are available via:
certbundle --help
🔧 Requirements
- Python 3.10+ 🐍
certbotonPATH, plus the DNS plugin matching--certbot-acme-challenge-methodwhen not usingwebroot
If certbot is missing, the command exits with code 127 and a one‑line error instead of a traceback.
🛠 Features
- Group multiple domains into a single SAN certificate
- Supports DNS-01 and HTTP-01 (webroot) ACME challenges
- Control DNS propagation wait time
- Wildcard domain support
- Test mode for Let's Encrypt staging environment
- Fully customizable via CLI parameters
⚙️ Certificate naming
Every certificate is named certbundle-<hash>-<counter>, where <hash> is the first 8 characters of the SHA‑256 over the sorted domain list and <counter> is a five‑digit batch number starting at 00001.
Because the hash covers the whole domain set, changing the set changes every certificate name and causes certbot to issue new certificates rather than renew the existing ones.
📊 Result classification
Each batch is classified from certbot's exit code and output:
| Status | Trigger |
|---|---|
updated |
certbot exited 0 |
skipped:no-change |
certificate not yet due for renewal |
skipped:already-issued |
already issued for this exact set of identifiers |
skipped:rate-limit |
too many certificates |
skipped:service-down |
the service is down for maintenance or had an internal error |
failed:other-error |
any other non‑zero exit |
Every batch runs to completion — a failing batch never aborts the remaining ones.
Exit codes
| Code | Meaning |
|---|---|
0 |
No batch failed. Skipped batches do not fail the run. |
1 |
At least one batch was classified failed. |
2 |
Invalid command line arguments. |
127 |
A required command is not installed. |
🧪 Development
make lint # ruff check + ruff format --check
make format # apply ruff format
make test # unit + integration tests
make test-unit
make test-integration
make test-e2e # install the package in a container and exercise the CLI
Tests run against the working tree — the Makefile puts src/ on PYTHONPATH, so no install is needed. The integration tests stub certbot on PATH, so no certificate is ever requested.
📜 License
This project is licensed under the MIT License.
👤 Author
Developed by Kevin Veen-Birkenbach
🌐 https://www.veen.world
Metadata
Release files for certbundle 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| certbundle-1.0.0.tar.gz | 13.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| certbundle-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.1 kB
Release files / certbundle-1.0.0.tar.gz
| Download URL | certbundle-1.0.0.tar.gz |
|---|---|
| Size | 13.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f25bb84c583b14259d3f1afac7c7a10a9daa22b0ed718b5e9eb99f0c3c3aed65
|
|
BLAKE2b-256 checksum How to use checksums |
5f2f0d7f8e21a681706d4d654457a4d12eb42e9bac20b83fbf3958dd2ef6b703
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.6
|
Release files / certbundle-1.0.0-py3-none-any.whl
| Download URL | certbundle-1.0.0-py3-none-any.whl |
|---|---|
| Size | 8.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7394b6c6d6e17a83c7c2c3285979954a83fcf91652a9d62144578c4203145580
|
|
BLAKE2b-256 checksum How to use checksums |
cf7dcf531f9631edbf442b5d7cc44195b4cfa6309394a1bd51eb6caf27ffd128
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.6
|