# traefik-certs-dumper turns Traefik's ACME store into PEM files

> traefik-certs-dumper reads the certificate data that Traefik keeps in acme.json or in a KV store and writes ordinary certificate and key files that other software can read. It supports Traefik v1, v2 and v3, can watch for changes and run a post hook, and ships as a binary or a container image.

**ldez/traefik-certs-dumper** — Dump ACME data from Traefik to certificates

- Repository: https://github.com/ldez/traefik-certs-dumper
- Stars: 637 · Forks: 35
- Language: Go
- License: NOASSERTION
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/ldez-traefik-certs-dumper

## Traefik stores certificates in a place other tools cannot read

The problem this solves comes from how Traefik handles ACME. Certificates obtained through Let's Encrypt or another ACME provider end up inside Traefik's own certificate store rather than as a directory of files, so anything else on the network that needs a certificate, a mail server, a synchronisation service or a monitoring agent, cannot simply point at a path.

traefik-certs-dumper reads that store and writes the files out in the conventional layout, a certificate and a private key per domain plus the account key, with a directory named certs for the keys and one named private for the certificate and the account key in the default output. The readme shows the simple case:

```bash
$ traefik-certs-dumper file --version v3
dump
├──certs
│  └──my.domain.com.key
└──private
   ├──my.domain.com.crt
   └──letsencrypt.key
```

Note the direction of travel. This tool does not obtain certificates, renew them or replace Traefik's ACME client. It is an export step, and it has to run against the same data Traefik uses, which means it needs read access to acme.json or to the KV store.

The audience is an operator who already runs Traefik and has hit the wall of wanting a plain certificate file for something else.

## Two source types and a watch mode for each

The supported sources are listed as two families. The first is the file, the acme.json that Traefik writes. The second is KV stores, with Consul, Etcd, Zookeeper and Boltdb named individually, each with its own subcommand and its own endpoint flag:

```bash
$ traefik-certs-dumper kv consul --endpoints localhost:8500
```

```bash
$ traefik-certs-dumper kv etcd --endpoints localhost:2379
```

```bash
$ traefik-certs-dumper kv boltdb --endpoints /the/path/to/mydb.db
```

```bash
$ traefik-certs-dumper kv zookeeper --endpoints localhost:2181
```

The file mode has a matching set of flags for source and destination, so the same tool reads from an explicit path and writes wherever you point it:

```bash
$ traefik-certs-dumper file --version v3 --source ./acme.json --dest ./dump/test
```

Watching is supported for the file and for the Consul, Etcd and Zookeeper stores, according to the feature list, and the readme is explicit that watching is what makes the hook mechanism useful. The distinction between watched and unwatched sources is a real constraint: a Boltdb source is listed as readable but not among the watched ones, so a consumer that needs to react to new certificates has to use one of the other sources or poll itself.

## Flat files or one directory per domain

The output format is configurable, and the second mode is the one most people want. The flat layout puts the domain in the filename, while the subdirectory layout gives each domain its own directory, and the readme shows what that looks like:

```bash
$ traefik-certs-dumper file --version v3 --domain-subdir=true
dump
├──my.domain.com
│  ├──certificate.crt
│  └──privatekey.key
└──private
   └──letsencrypt.key
```

The names are configurable too, which is the part that makes the tool usable with software that expects particular filenames. Both the extension and the base name can be changed:

```bash
$ traefik-certs-dumper file --version v3 --domain-subdir --crt-ext=.pem --key-ext=.pem
```

```bash
$ traefik-certs-dumper file --version v3 --domain-subdir --crt-name=fullchain --key-name=privkey
```

So a consumer that wants a fullchain.pem and a privkey.pem per domain, which is the convention many servers and load balancers follow, is a flag away. The feature list also mentions custom names and extensions, which covers the cases where the default certificate.crt and privatekey.key are not what the other side expects.

## A hook that fires only when the data actually changes

The post hook is the reason to run this in watch mode rather than on a schedule. The readme states that the hook runs only with watch mode and only if the data source changes, and it can be either a one line string or a file for more complex post processing.

The compose example shows the intended shape, and it also shows how the timing problem is solved. The entrypoint waits in a loop until acme.json exists and contains at least one certificate, checking with jq, and only then starts the dumper in watch mode with the hook attached:

```yml
services:
  traefik-certs-dumper:
    image: ldez/traefik-certs-dumper:v2.9.3
    container_name: traefik-certs-dumper
    entrypoint: sh -c '
      while ! [ -e /data/acme.json ]
      || ! [ `jq ".[] | .Certificates | length" /data/acme.json | jq -s "add" ` != 0 ]; do
      sleep 1
      ; done
      && traefik-certs-dumper file --version v2 --watch
        --source /data/acme.json --dest /data/certs
        --post-hook "sh /hook.sh"'
    labels:
      traefik.enable: false
    volumes:
      - ./letsencrypt:/data
      - ./hook.sh:/hook.sh
```

Two details in that snippet are worth copying. The traefik.enable: false label keeps Traefik itself from routing traffic to the dumper, and the hook file is mounted from the host so the post-processing script lives outside the container image.

The hook receives no arguments in this example, so a script that reloads a service has to know which service to reload, which means one dumper instance per consumer or a hook that discovers its target.

## Installing as a script, a binary or a container

There are three documented ways to get the tool.

The first is a download script intended for CI use, which pipes a small generator script into bash and passes the destination directory and a version:

```bash
curl -sfL https://raw.githubusercontent.com/ldez/traefik-certs-dumper/master/godownloader.sh | bash -s -- -b $(go env GOPATH)/bin v2.9.3
```

The second is the plain binary route. Download the archive for your OS and architecture from the releases page, unzip it and put traefik-certs-dumper in your PATH. The third is the container image, which is published on Docker Hub:

```bash
docker run ldez/traefik-certs-dumper:<tag_name>
```

The version argument in the download command is the tag you want, and the compose example pins an image tag of v2.9.3, so pinning a tag rather than tracking latest is the pattern the project's own examples follow.

One gap in the documentation is worth flagging. Compose examples are linked for Traefik v1 and for Traefik v2, while the entry for Traefik v3 is marked as a to do item in the readme. The tool itself understands all three versions, since the version is a flag on every command, but the ready made container setup is only documented for the two older ones.

## A Go codebase with cobra, viper and lego underneath

The implementation is Go, and the module is published under a version 2 path, github.com/ldez/traefik-certs-dumper/v2, requiring Go 1.26.0. The dependency list explains what the tool is doing more clearly than the feature bullets do.

github.com/spf13/cobra and github.com/spf13/viper provide the command structure and the configuration handling, which is why the flags look consistent across the file and kv subcommands. github.com/go-acme/lego/v5 is the ACME library, and github.com/charmbracelet/lipgloss is a terminal styling library, which suggests formatted, coloured terminal output rather than plain log lines.

The KV stores are reached through a family of small adapters under github.com/kvtools, one per backend, with separate modules for the etcd v2 and v3 clients and a valkeyrie module for Zookeeper. github.com/fsnotify/fsnotify provides the file watching, and github.com/cenkalti/backoff/v5 is a retry helper, which is what a watcher needs when the KV store or the file is briefly unavailable.

The Makefile shows the project's own discipline. The default target runs clean, checks, test and build in that order, the checks target runs golangci-lint, the test target runs the Go test suite with coverage, and the build target stamps the version, commit and build date into the binary through linker flags. There is also a doc target that runs go run . doc, which is how the documentation in the docs/ directory is generated.

## Frequent releases, a custom licence and no homepage

The project listing classifies the licence as Other, described as a custom licence that GitHub cannot classify, with a pointer to the LICENSE file at the top of the repository. There is also a LICENSE file in the tree, so the text is available, but anyone redistributing this tool inside a product should read it rather than assume a standard licence from the badge.

Release activity is frequent and recent. The tags include v2.11.4 and v2.11.3 on the same day, 2026-06-15, and v2.11.2 on 2026-04-16, and the last push was on 2026-10-02 with the repository not archived. A project with patch releases every few weeks is a good sign for a tool that sits in a certificate renewal path, and it also means you should pin a version rather than track the latest tag blindly.

There is no homepage field in the project listing, so the repository readme and the docs/ directory are the reference. The documentation is split by subcommand, with separate files for the general tool, the file mode and the kv mode, which is a sensible arrangement, and the repository also carries integrationtest/, hook/, contrib/ and cmd/ directories alongside the main.go entry point.

## Conclusion

Adopt traefik-certs-dumper when something other than Traefik needs the certificates Traefik manages, for example a mail server or an appliance that expects separate certificate and key files, and you want the conversion to run inside the same container stack. Skip it if your consumer can read Traefik's own certificate resolver output, or if you cannot accept a tool that writes private keys to disk in a readable form. Before deploying, run the file mode once against a copy of acme.json, read the LICENSE file because the project is listed with a custom licence GitHub cannot classify, and pick a release tag, since the container image and the binary have to agree on the Traefik version passed with --version.

## FAQ

### What does traefik-certs-dumper do?

It dumps ACME data from Traefik to certificate files. It reads the acme.json file or a KV store and writes a certificate and a key per domain, with a directory layout that can be flat or one subdirectory per domain.

### Which Traefik versions are supported?

The feature list states support for Traefik v1, v2 and v3, and the version is passed on the command line with a flag such as --version v3. Ready made docker-compose examples are linked for v1 and v2, while the v3 example is still marked as a to do item in the readme.

### How do I install traefik-certs-dumper?

You can pipe the godownloader.sh script from the repository into bash with a destination and a version, download a pre-compiled binary from the releases page and put it in your PATH, or run the container image with docker run ldez/traefik-certs-dumper:<tag_name>.

### What can I configure about the output files?

The layout can be flat or one directory per domain with the domain-subdir flag, and the file names and extensions can be changed with crt-ext, key-ext, crt-name and key-name. The default names in the domain directory are certificate.crt and privatekey.key.

### When does the post hook run?

The hook runs only in watch mode and only when the data source changes. It can be a one line string passed to the post-hook flag or a mounted file, and the compose example in the readme waits until acme.json exists and holds a certificate before starting the dumper.

### Can traefik-certs-dumper read from a KV store?

Yes. The kv subcommand supports Consul, Etcd, Zookeeper and Boltdb, each with an endpoints flag, for example traefik-certs-dumper kv consul --endpoints localhost:8500. Watching is supported for the file and for Consul, Etcd and Zookeeper.

## Sources

- [Issues](https://github.com/ldez/traefik-certs-dumper/issues)
- [ldez/traefik-certs-dumper on GitHub](https://github.com/ldez/traefik-certs-dumper)
- [README](https://github.com/ldez/traefik-certs-dumper/blob/main/README.md)
- [Releases](https://github.com/ldez/traefik-certs-dumper/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ldez-traefik-certs-dumper
