Skip to main content

traefik-certificate-exporter

Overview

Github Tags PyPi Version Docker

This tool can be used to extract acme certificates (ex: lets encrupt) from traefik json files. The tool is design to watch for changes to a folder for any files that match a filespec (defaults to *,json however can be set to a specific file name) and when changes are detected it will process the file and extract any certificates that are in it to the specified output path

Supported stores: acme.json from any RFC 8555 CA (Let's Encrypt, ZeroSSL, Google Trust Services, step-ca, ...) written by Traefik v1.7, v2 or v3; the shape is detected from the file's keys, not the CA URL, and each resolver is exported independently.

Installation

Python Script/Tool

Installation can be done via the python package installer tool pip

pip install traefik-certificate-exporter

Usage

usage: traefik-certificate-exporter [-h] [-c PATH] [-d PATH] [-w] [-fs GLOB] [-o PATH] [--traefik-resolver-id RESOLVER] [--flat] [--restart-container] [--dry-run] [-r] [--include-resolvername-in-outputpath]
                                    [--pkcs12-passphrase PASSPHRASE] [--post-export-command COMMAND] [-ll LEVEL] [-id [DOMAIN ...] | -xd [DOMAIN ...]]

Extract traefik letsencrypt certificates.

options:
  -h, --help            show this help message and exit
  -c PATH, --config-file PATH
                        the configuration file to load (default: config.yaml)
  -d PATH, --data-path PATH
                        the path that contains the acme json files
  -w, --watch-for-changes
                        If specified, monitor and watch for changes to acme files
  -fs GLOB, --file-spec GLOB
                        file that contains the traefik certificates
  -o PATH, --output-path PATH
                        The folder to exports the certificates in to
  --traefik-resolver-id RESOLVER
                        Traefik certificate-resolver-id.
  --flat                If specified, all certificates into a single folder
  --restart-container   If specified, any container that are labeled with 'com.github.ravensorb.traefik-certificate-exporter.domain-restart=<DOMAIN>' will be restarted, only on change: when an exported certificate file
                        for one of its domains is newer than the container's start. Multiple domains can be separated by ','
  --dry-run             Don't write files and do not restart docker containers.
  -r, --run-at-start    Runs Export immediately on start (used with watch-for-changes).
  --include-resolvername-in-outputpath
                        Added the resolvername in the path used to export the certificates (ignored if flat is specified).
  --pkcs12-passphrase PASSPHRASE
                        Passphrase used to encrypt the exported PKCS12 (.pfx) file with AES-256/SHA-256 (readable by Windows 10 1709+/Server 2019+, OpenSSL 3, current macOS). The .pfx always includes the intermediates.
                        Empty or unset: unencrypted.
  --post-export-command COMMAND
                        Shell-like command line to run after an export pass, only on change: when at least one domain's exported files changed (no shell involved -- parsed with shlex, run via subprocess with a fixed
                        30s timeout). The changed domain names are exposed to it as a comma-separated TRAEFIK_CERTIFICATE_EXPORTER_EXPORTED_DOMAINS environment variable. Treated as a secret: log lines name only its
                        program. Skipped entirely when --dry-run is set.
  -ll LEVEL, --log-level LEVEL
                        Set the logging level, one of: DEBUG, INFO, WARNING, ERROR, CRITICAL. When not given, TRAEFIK_CERTIFICATE_EXPORTER_LOGGING_LEVEL is used (case-insensitive); otherwise the default is INFO.
  -id [DOMAIN ...], --include-domains [DOMAIN ...]
                        If specified, only certificates that match domains in this list will be extracted
  -xd [DOMAIN ...], --exclude-domains [DOMAIN ...]
                        If specified. certificates that match domains in this list will be ignored

Settings come from four layers, and for every setting the order is CLI > env > config file > defaults: a CLI flag beats a TRAEFIK_CERTIFICATE_EXPORTER_* environment variable, which beats the config file, which beats the packaged default. In the image this makes the environment the run-mode switch: the image sets TRAEFIK_CERTIFICATE_EXPORTER_SETTINGS_WATCHFORCHANGES=true, so watchforchanges: false in /config/config.yaml alone does not select run-once (docker/README.md).

Argument parsing is strict: an unrecognised flag (or a stray positional argument) exits immediately with code 2, naming the closest valid flag when there is one (Did you mean '--data-path'?) and pointing at --help. This happens before logging is configured, so nothing is written to the log file. If the container crash-loops at start-up with no log output, check your command:/args: override for a typo and read the container's stderr.

Examples

Watch the letsencrypt folder for any changes to files matching acme-*.json and export any certs managed by the resolver called "resolver-http"

Script

Run it once and then exit

traefik-certificate-exporter \
                            -d /mnt/traefik-data/letsencrypt \
                            -o /mnt/certs \
                            -fs "acme-*.json" \
                            -r

Run it and watch for changes to the files

traefik-certificate-exporter \
                            -d /mnt/traefik-data/letsencrypt \
                            -o /mnt/certs \
                            -fs "acme-*.json" 
                            -w

Post-Export Hook

--post-export-command (or settings.postexportcommand / TRAEFIK_CERTIFICATE_EXPORTER_SETTINGS_POSTEXPORTCOMMAND) runs a shell-like command line after an export pass, only on change: when at least one domain's exported files changed in that pass -- useful for copying/chowning/moving an exported certificate into another service's expected location. A pass that changed nothing (including an exporter restart over an unchanged store) runs no hook and logs No certificate changed; no hook or notification once at INFO (ADR-0045). It is unset by default (purely additive; no behavior change if you don't set it).

  • The command is parsed with Python's shlex and executed directly (never through a shell), so it is not vulnerable to shell injection, but it also doesn't support shell features like pipes, globs, or $VAR expansion.
  • The changed domain names (only those) are exposed to it as a comma-separated TRAEFIK_CERTIFICATE_EXPORTER_EXPORTED_DOMAINS environment variable; it is never empty, because the hook does not run when nothing changed.
  • The command is treated as a secret, because it often carries a credential (an Authorization header, a webhook token): it is redacted in the DEBUG settings dumps, and the hook's log lines name only the program (argv[0]), never its arguments. When the hook fails, only the last line of its stderr is logged, with its arguments masked and control characters stripped; it is not redacted beyond that, so keep credentials out of what it prints in any other form.
  • The export pass's TRAEFIK_CERTIFICATE_EXPORTER_CORRELATION_ID is exposed too, so a hook that logs elsewhere can be joined to the exporter's own log lines for that pass.
  • A fixed 30 second timeout applies; a command that doesn't return in time is killed and the timeout is logged as an error.
  • Skipped entirely when --dry-run is set, consistent with dry-run suppressing file writes and container restarts.
  • A non-zero exit or timeout is logged as an error but never crashes the watch loop.
traefik-certificate-exporter \
                            -d /mnt/traefik-data/letsencrypt \
                            -o /mnt/certs \
                            -w \
                            --post-export-command "/usr/local/bin/notify-service.sh"

Logging and tracing an export pass

Each export pass (the start-up sweep, and each debounced pass the watcher runs) is tagged with a correlation_id — a UUID4 shared by every log line that pass produces, from the detected file change through the export, the post-export hook and the notification. Grep one value to see the whole pass.

The rotating log file is JSON, so correlation_id is a field there already. Console output is coloured human-readable text by default, with the id in brackets after the level; set TRAEFIK_CERTIFICATE_EXPORTER_LOGGING_CONSOLEJSON=true to emit the console stream as JSON instead (useful when docker logs is being shipped to a log aggregator). A - means the line was emitted outside any export pass.

The log level is -ll/--log-level; when that is not given, TRAEFIK_CERTIFICATE_EXPORTER_LOGGING_LEVEL (case-insensitive: DEBUG, INFO, WARNING, ERROR, CRITICAL) is used, and otherwise INFO. The container's command line is fixed, so the environment variable is how to change verbosity there (-e TRAEFIK_CERTIFICATE_EXPORTER_LOGGING_LEVEL=DEBUG). An invalid value falls back to INFO with one WARNING, and one INFO line at start-up names the effective level and its source (cli, env or default).

See docs/operational.md.

Releases and images

  • Versions come from git tags. A release is a vX.Y.Z tag on main; the package and the image carry that version. Every push to main between releases publishes a dev build (X.Y.Z.devN for the package, X.Y.Z-dev.N and dev for the image).
  • Python package: PyPI (releases), TestPyPI (dev builds) and the Gitea package index.
  • Image: GitHub Container Registry, Docker Hub and the Gitea container registry, built for amd64 and arm64. latest and X.Y.Z / X.Y follow releases (and X from 1.0.0 on; no bare 0 tag is written); dev follows main.
  • A destination is enabled only when its PUBLISH_* token secret and every PUBLISH_* variable it needs are set, so a fork or a mirror publishes only where it has been configured to. A partly configured destination is skipped with one warning naming what is missing; an unconfigured one is silent. The Gitea package index and the Gitea container registry share PUBLISH_GITEA_TOKEN but can be enabled separately.
  • Where the publish secrets live. One forge's run publishes a whole release. On GitHub the PUBLISH_* secrets live in the dev or release environment, never at repository level (ADR-0039). On Gitea they are repository-level secrets, present only when Gitea is the publishing forge, with the mitigations of CI spine AD-13: protected main and v* tags, project-scoped tokens, rotation after any untrusted push. The forge settings to apply are the checklist in docs/ci/pipeline-walkthrough.md.

How the pipeline works, and how to set it up: docs/ci/pipeline-walkthrough.md.

Upgrading from an earlier release (what changes in each version and what to do about it): docs/upgrading.md.

Credits

This tool is HEAVLY influenced by the excellent work of DanielHuisman and Marc Brückner

Export notifications

Set appriseurls in your config file to be notified when a certificate changes — email, chat, or a generic webhook, via Apprise:

settings:
  appriseurls:
    - "tgram://<bot-token>/<chat-id>"
    - "mailto://user:pass@example.com"

There is no command-line flag for this setting, matching domains — the other list-valued setting — and from the environment it takes indexed keys (..._APPRISEURLS_0, ..._APPRISEURLS_1) rather than a comma-separated string, because a comma is legal inside an Apprise URL.

A notification means a certificate changed: it is sent only on change, when at least one domain's exported files changed in the pass, and it lists only those domains. Each built file is compared with the one on disk and an unchanged file is not rewritten, so its modification time stays stable (ADR-0043). A pass that changed nothing — an exporter restart over an unchanged store, or a pass that only recovers a lost container restart — sends nothing (ADR-0045). Delivery is fire-and-forget — a failure is logged and stops nothing. For something that must succeed, use postexportcommand, which blocks and reports its exit code.

See docker/README.md for the container setup.

Metadata

Release files for traefik-certificate-exporter 0.3.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 traefik-certificate-exporter 0.3.0
File Size Uploaded
traefik_certificate_exporter-0.3.0.tar.gz 65.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for traefik-certificate-exporter 0.3.0
File Interpreter ABI Platform
traefik_certificate_exporter-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 135.2 kB

Release files / traefik_certificate_exporter-0.3.0.tar.gz

Download URL traefik_certificate_exporter-0.3.0.tar.gz
Size 65.9 kB
Tags Source
SHA-256 checksum
How to use checksums
39fbc9c41be12b367b9a5c1716d8aa54a569803b0de0cd67f23832271f8be170
BLAKE2b-256 checksum
How to use checksums
65d761076f6218f7979daaa17638ad784e02419de45abeab4005f68eb1cbd7a1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8

Release files / traefik_certificate_exporter-0.3.0-py3-none-any.whl

Download URL traefik_certificate_exporter-0.3.0-py3-none-any.whl
Size 69.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6f8dd26aba4d67316504fb9902471f05769007ba5a041ab791a0f0b9240a3df5
BLAKE2b-256 checksum
How to use checksums
753454d402aef7ac52f3d2da3ffa81cf27904b507d2a9948d9dd4746f53b59b6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.8
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