Skip to main content

📦 Certbot Domain Bundler

GitHub Sponsors Patreon Buy Me a Coffee PayPal

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+ 🐍
  • certbot on PATH, plus the DNS plugin matching --certbot-acme-challenge-method when not using webroot

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)

Source distribution for certbundle 1.0.0
File Size Uploaded
certbundle-1.0.0.tar.gz 13.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for certbundle 1.0.0
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page