|
|
||
|---|---|---|
| .forgejo/workflows | ||
| src | ||
| .cursorignore | ||
| .gitignore | ||
| Dockerfile | ||
| LICENSE | ||
| README.md | ||
traefik-certdump
Lightweight service that reads Traefik’s acme.json under the letsencrypt resolver block, extracts certificate and key as PEM files, and writes them to disk on a schedule. It exposes a local HTTP /health endpoint (for Docker HEALTHCHECK or similar).
The final image is based on scratch and contains only the statically linked Go binary.
How it works
- At startup, the YAML file from
--config-file(if any) and environment variables are loaded once. On each cycle, only Traefik’sacme.jsonis re-read when the file has changed or had not yet been loaded successfully. - The expected JSON shape is
{"letsencrypt":{"certificates":[...]}}. Each entry hasdomain(main,sans),certificate, andkeyas base64 (as Traefik stores them). Only this path is read: other top-level resolver keys are ignored. - For each configured destination, a certificate whose
domain.mainexactly matches the requested domain is used (same string as in your config). SANs are not used for matching. - Chain and key are written atomically (temp file + rename), creating directories if missing.
- When temporal validity checks are enabled, a certificate that is not yet valid or already expired causes the export cycle to fail and the health endpoint returns
503until the situation is resolved. GET http://127.0.0.1:8080/healthreturns200with JSON{"status":"healthy","message":"...","lastUpdate":"RFC3339"}when the last cycle succeeded; otherwise503with"status":"unhealthy".
The first read/write cycle runs immediately after startup; later cycles use POLL_SECONDS / poll_seconds.
Docker
The image default command is /traefik-certdump. The Dockerfile defines a HEALTHCHECK that runs /traefik-certdump health, i.e. an HTTP request to http://127.0.0.1:8080/health like the main process serves.
For full docker run examples see Examples.
Configuration
Environment variables (single domain)
Without --config-file, one destination is defined with ACME_DOMAIN, ACME_CERT_OUT, and ACME_KEY_OUT (absolute PEM paths). JSON_PATH is still required unless the same path is set as json_path in YAML used with --config-file.
If any of the three ACME_* variables is set, all three must be set.
YAML file (--config-file)
The file is read once at startup; only acme.json is observed on later intervals. For multiple domains, use YAML, for example:
docker run --rm \
-v /path/to/config.yaml:/config.yaml:ro \
-v ... \
docker.asperti.com/paspo/traefik-certdump:latest \
--config-file /config.yaml
Schema (all top-level keys are optional; you need at least one destination in the file and/or via env):
| YAML key | Type | Description |
|---|---|---|
json_path |
string | Path to acme.json. |
poll_seconds |
int | Polling interval in seconds (positive; defaults to 3600 if omitted). |
disable_expiry_check |
bool | Skips temporal validity checks on the certificate (defaults to false if omitted). |
domains |
list | Each item: domain, cert_path, key_path. |
Precedence: for json_path, poll_seconds, and disable_expiry_check, if the matching environment variable is set and non-empty, it overrides YAML. Destinations from the file and from the env triplet are merged; if the same domain appears twice, the last registration wins (the env triplet is applied after YAML entries).
If POLL_SECONDS is unset and poll_seconds is missing from YAML (or not positive), 3600 is used.
Examples
Single domain (env only)
docker run --rm \
-v /var/lib/docker/volumes/traefik_acme/_data/acme.json:/acme.json:ro \
-v /certs/out:/out \
-e JSON_PATH=/acme.json \
-e ACME_DOMAIN=example.com \
-e ACME_CERT_OUT=/out/fullchain.pem \
-e ACME_KEY_OUT=/out/privkey.pem \
docker.asperti.com/paspo/traefik-certdump:latest
Multiple domains (YAML)
Example config.yaml:
json_path: /acme.json
poll_seconds: 3600
disable_expiry_check: false
domains:
- domain: www.example.com
cert_path: /certs/www/fullchain.pem
key_path: /certs/www/privkey.pem
- domain: api.example.com
cert_path: /certs/api/fullchain.pem
key_path: /certs/api/privkey.pem
docker run --rm \
-v /var/lib/docker/volumes/traefik_acme/_data/acme.json:/acme.json:ro \
-v /path/config.yaml:/config.yaml:ro \
-v /certs/www:/certs/www \
-v /certs/api:/certs/api \
docker.asperti.com/paspo/traefik-certdump:latest \
--config-file /config.yaml
Build from source
cd src
go build -o traefik-certdump ./cmd/traefik-certdump
Static binary similar to the image:
CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o traefik-certdump ./cmd/traefik-certdump
Subcommand for manual probing (same as the image healthcheck):
./traefik-certdump health
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
JSON_PATH |
yes* | — | Absolute path to Traefik’s acme.json. |
POLL_SECONDS |
no | 3600 |
Seconds between cycles; must be a positive integer. |
ACME_DOMAIN |
no* | — | With ACME_CERT_OUT and ACME_KEY_OUT, defines one destination; with --config-file, can add another. |
ACME_CERT_OUT |
no* | — | Full path to the PEM chain file. |
ACME_KEY_OUT |
no* | — | Full path to the PEM private key file. |
DISABLE_EXPIRY_CHECK |
no | false |
If true, skips temporal validity checks on the certificate. |
* JSON_PATH: required unless json_path is set in the YAML used with --config-file. For destinations: without --config-file, ACME_DOMAIN, ACME_CERT_OUT, and ACME_KEY_OUT are required; with --config-file, you need domains in the file and/or the same env triplet.
License
See the LICENSE file in the repository.