How to Build and Use Private Plugins with Traefik Proxy 2.5
This guide explains how to create Docker images that bundle custom or demo plugins for Traefik Proxy 2.5, covering local, public, and private Git sources, Docker‑Compose development setups, and optional local DNS configuration with dnsmasq for testing complex routing rules.
Overview
Traefik Proxy is a modular router that can load middleware plugins from a /plugins-local directory starting with version 2.5, without requiring Traefik Pilot.
Build a Traefik image with the demo plugin
Create a temporary directory and add a Dockerfile.demo that clones the demo plugin repository and copies it into the image.
# Dockerfile.demo
FROM alpine:3
ARG PLUGIN_MODULE=github.com/traefik/plugindemo
ARG PLUGIN_GIT_REPO=https://github.com/traefik/plugindemo.git
ARG PLUGIN_GIT_BRANCH=master
RUN apk add --update git && \
git clone ${PLUGIN_GIT_REPO} /plugins-local/src/${PLUGIN_MODULE} \
--depth 1 --single-branch --branch ${PLUGIN_GIT_BRANCH}
FROM traefik:v2.5
COPY --from=0 /plugins-local /plugins-localBuild and tag the image:
docker build -f Dockerfile.demo --tag traefik-with-demo-plugin .Run the container to verify plugin loading:
docker run --rm -it traefik-with-demo-plugin \
--log.level=DEBUG \
--experimental.localPlugins.demo.moduleName=github.com/traefik/plugindemoThe logs show the plugin being loaded. Changing moduleName to a non‑existent module causes an immediate error.
Build an image with a custom plugin
Fork the demo repository, edit the source, and modify the
.traefik.yml importline to match the new repository name. No compilation is required; Traefik interprets the Go source at runtime using Yaegi.
Build from a public Git repository
Export build‑time variables:
export DOCKER_IMAGE=traefik-with-my-plugin
export PLUGIN_MODULE=github.com/YOUR_NAME/YOUR_REPOSITORY
export PLUGIN_GIT_REPO=https://github.com/YOUR_NAME/YOUR_REPOSITORY.git
export PLUGIN_GIT_BRANCH=masterCreate Dockerfile.public using the same multi‑stage pattern:
# Dockerfile.public
FROM alpine:3
ARG PLUGIN_MODULE
ARG PLUGIN_GIT_REPO
ARG PLUGIN_GIT_BRANCH
RUN apk update && \
apk add git && \
git clone ${PLUGIN_GIT_REPO} /plugins-local/src/${PLUGIN_MODULE} \
--depth 1 --single-branch --branch ${PLUGIN_GIT_BRANCH}
FROM traefik:v2.5
COPY --from=0 /plugins-local /plugins-localBuild the image:
docker build -f Dockerfile.public \
--tag ${DOCKER_IMAGE} \
--build-arg PLUGIN_MODULE \
--build-arg PLUGIN_GIT_REPO \
--build-arg PLUGIN_GIT_BRANCH .Build from a private Git repository
Docker BuildKit (Docker ≥ 18.09) and SSH forwarding are required.
export DOCKER_BUILDKIT=1
export DOCKER_IMAGE=traefik-with-my-plugin
export PLUGIN_MODULE=github.com/YOUR_NAME/YOUR_REPOSITORY
export [email protected]:YOUR_NAME/YOUR_REPOSITORY.git
export PLUGIN_GIT_BRANCH=masterCreate Dockerfile.private with experimental syntax and an --mount=type=ssh step:
# syntax=docker/dockerfile:1.0.0-experimental
FROM alpine:3
ARG PLUGIN_MODULE
ARG PLUGIN_GIT_REPO
ARG PLUGIN_GIT_BRANCH
RUN apk add --update git openssh && \
mkdir -m 700 /root/.ssh && \
touch -m 600 /root/.ssh/known_hosts && \
ssh-keyscan github.com > /root/.ssh/known_hosts
RUN --mount=type=ssh git clone \
--depth 1 --single-branch --branch ${PLUGIN_GIT_BRANCH} \
${PLUGIN_GIT_REPO} /plugins-local/src/${PLUGIN_MODULE}
FROM traefik:v2.5
COPY --from=0 /plugins-local /plugins-localBuild the image with SSH forwarding:
docker build -f Dockerfile.private \
--ssh default \
--tag ${DOCKER_IMAGE} \
--build-arg PLUGIN_MODULE \
--build-arg PLUGIN_GIT_REPO \
--build-arg PLUGIN_GIT_BRANCH .Note: the --ssh flag is not supported in docker‑compose, so the image must be built with docker build before using docker‑compose up.
Docker‑compose as a plugin development environment
Create a simple Dockerfile that adds the plugin source to /plugins-local/src:
# Dockerfile
FROM traefik:v2.5
ARG PLUGIN_MODULE=github.com/traefik/plugindemo
ADD . /plugins-local/src/${PLUGIN_MODULE}Create a .env file:
# .env
PLUGIN_NAME=demo
PLUGIN_MODULE=github.com/traefik/plugindemo
WHOAMI_TRAEFIK_HOST=whoami.example.comDefine docker-compose.yaml to run Traefik with the local plugin and a whoami test service:
# docker-compose.yaml
version: "3.3"
services:
traefik-proxy:
build:
context: .
args:
PLUGIN_MODULE: ${PLUGIN_MODULE}
restart: unless-stopped
networks: [traefik-proxy]
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--api.dashboard=true"
- "--api.insecure=true"
- "--experimental.localPlugins.${PLUGIN_NAME}.moduleName=${PLUGIN_MODULE}"
ports:
- "80:80"
- "443:443"
- "127.0.0.1:9000:9000"
volumes:
- "traefik-proxy:/data"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`${WHOAMI_TRAEFIK_HOST}`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.middlewares=whoami-demo"
- "traefik.http.middlewares.whoami-demo.plugin.${PLUGIN_NAME}.headers.DoesPluginWork=YES"
- "traefik.http.routers.whoami.tls.certresolver=default"
whoami:
image: traefik/whoami
networks: [traefik-proxy]
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`${WHOAMI_TRAEFIK_HOST}`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.middlewares=whoami-demo"
- "traefik.http.middlewares.whoami-demo.plugin.${PLUGIN_NAME}.headers.DoesPluginWork=YES"
- "traefik.http.routers.whoami.tls.certresolver=default"
volumes:
traefik-proxy:Add a .dockerignore to exclude the Git history: .git Start the stack: docker-compose up Test with: curl -k https://whoami.example.com The response should contain the header DoesPluginWork: YES, confirming that the plugin is active.
Configure a local DNS service for development
Use dnsmasq to provide wildcard A records for many sub‑domains, avoiding repeated edits to /etc/hosts.
# /etc/dnsmasq.conf
interface=lo
listen-address=::1,127.0.0.1
bind-interfaces
cache-size=1000
server=1.1.1.1
server=1.0.0.1
address=/example.com/127.0.0.1Enable the service (systemd example): sudo systemctl enable --now dnsmasq.service Point the resolver to the local server by setting nameserver 127.0.0.1 in /etc/resolv.conf. Make the file immutable to prevent overwrites: chattr +i /etc/resolv.conf Verify with dig, drill or nslookup; the answer should resolve the wildcard domain to the Docker host IP.
References
https://doc.traefik.io/traefik/middlewares/overview/
https://doc.traefik.io/traefik-pilot/
https://github.com/traefik/plugindemo
https://github.com/traefik/traefik/pull/8224
https://docs.docker.com/develop/develop-images/build_enhancements
https://github.com/docker/compose/issues/7025
https://traefik.io/blog/using-private-plugins-in-traefik-proxy-2-5/
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
