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

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

certbundle-1.0.0.tar.gz (13.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

certbundle-1.0.0-py3-none-any.whl (8.5 kB view details)

Uploaded Python 3

File details

Details for the file certbundle-1.0.0.tar.gz.

File metadata

  • Download URL: certbundle-1.0.0.tar.gz
  • Upload date:
  • Size: 13.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for certbundle-1.0.0.tar.gz
Algorithm Hash digest
SHA256 f25bb84c583b14259d3f1afac7c7a10a9daa22b0ed718b5e9eb99f0c3c3aed65
MD5 5152fff5f89c52d8daecb0dc5e569f0d
BLAKE2b-256 5f2f0d7f8e21a681706d4d654457a4d12eb42e9bac20b83fbf3958dd2ef6b703

See more details on using hashes here.

File details

Details for the file certbundle-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: certbundle-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 8.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for certbundle-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7394b6c6d6e17a83c7c2c3285979954a83fcf91652a9d62144578c4203145580
MD5 a643f2fd02dd20ebd2131cf46b4da06f
BLAKE2b-256 cf7dcf531f9631edbf442b5d7cc44195b4cfa6309394a1bd51eb6caf27ffd128

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page