Skip to content

Local & Air-Gapped Deployment (/plugins-local) ​

This guide explains how to install, develop, and run RouteWarden in air-gapped, offline, and local enterprise environments using Traefik's native /plugins-local mechanism without external downloads from GitHub or the Traefik Plugin Catalog.


1. How Traefik Local Plugins Work ​

Traefik allows loading plugins directly from the host filesystem using the experimental.localPlugins configuration key. This provides several key advantages:

  • 100% Offline / Air-Gapped: Traefik never calls out to plugins.traefik.io or GitHub, eliminating network timeouts, proxy issues, or firewall blocks.
  • Zero Build Steps: Traefik's embedded Yaegi Go interpreter compiles the .go source files directly in memory at launch.
  • Instant Live Iteration: Code changes take effect immediately on container restart without rebuilding container images or tagging releases.

When using localPlugins, Traefik strictly requires the source code to be mounted into a specific path matching Go's import hierarchy under /plugins-local/src/:

plugins-local/└── src/    └── github.com/        └── routewarden/            └── traefik-warden/                ├── .traefik.yml                ├── config.go                ├── ip_filter.go                ├── path_normalizer.go                ├── response_handler.go                └── routewarden.go

2. Local Traefik Configuration Preview ​

# traefik.yml (Static)experimental:  localPlugins:    routewarden:      moduleName: github.com/routewarden/traefik-warden# dynamic_conf.yml (Dynamic Middleware & Router)http:  middlewares:    local-warden:      plugin:        routewarden:          enabled: true          enableDefaultPatterns: true          response:            mode: json            statusCode: 403            body: '{"error":"Forbidden","environment":"local-dev"}'  routers:    app-router:      rule: "Host(`localhost`)"      entryPoints:        - web      middlewares:        - local-warden      service: app-service

3. Local Docker Compose Setup ​

Here is a complete, ready-to-run docker-compose.yml for developing and testing RouteWarden locally:

services:  traefik:    image: traefik:latest    command:      - "--api.insecure=true"      - "--providers.docker=true"      - "--providers.docker.exposedbydefault=false"      - "--entrypoints.web.address=:80"            # Declare RouteWarden as a LOCAL plugin:      - "--experimental.localPlugins.routewarden.modulename=github.com/routewarden/traefik-warden"            # Log level debug helps verify plugin loading      - "--log.level=DEBUG"    ports:      - "80:80"      - "8080:8080" # Traefik Web UI & Middleware Explorer    volumes:      - "/var/run/docker.sock:/var/run/docker.sock:ro"      # Mount your local repository directory into Traefik's plugins-local path:      - ".:/plugins-local/src/github.com/routewarden/traefik-warden:ro"  webapp:    image: nginx:alpine    labels:      - "traefik.enable=true"      - "traefik.http.routers.webapp.rule=Host(`localhost`)"      - "traefik.http.routers.webapp.entrypoints=web"      - "traefik.http.routers.webapp.middlewares=local-warden"      # Middleware configuration      - "traefik.http.middlewares.local-warden.plugin.routewarden.enabled=true"      - "traefik.http.middlewares.local-warden.plugin.routewarden.enableDefaultPatterns=true"      - "traefik.http.middlewares.local-warden.plugin.routewarden.response.mode=json"      - "traefik.http.middlewares.local-warden.plugin.routewarden.response.statusCode=403"      - 'traefik.http.middlewares.local-warden.plugin.routewarden.response.body={"error":"Forbidden","environment":"local-dev"}'

4. Step-by-Step Local Walkthrough ​

Step 1: Start the Cluster ​

From the root of the RouteWarden repository:

docker compose up

Step 2: Verify Plugin Initialization ​

Watch the Traefik startup logs. You should see Traefik's Yaegi interpreter successfully compiling the local plugin:

level=info msg="Loading plugin: routewarden with module: github.com/routewarden/traefik-warden"level=info msg="Plugin routewarden loaded successfully"

Step 3: Inspect via Traefik Dashboard ​

Open your browser to: http://localhost:8080/dashboard/#/http/middlewares

You will see local-warden@docker listed with its active configuration.

Step 4: Test Blocking Live ​

curl -I http://localhost/# HTTP/1.1 200 OK

Step 5: Live Code Iteration ​

Because the repository root is mounted with - .:/plugins-local/src/github.com/routewarden/traefik-warden:ro, whenever you modify Go code, simply restart Traefik to recompile:

docker compose restart traefik

(No Docker rebuild or external plugin download required!)


5. Air-Gapped & Enterprise Production Setup ​

When deploying to production clusters with no public internet access (e.g., banking, healthcare, government, or private VPCs), follow these steps to bundle RouteWarden offline.

Step 1: Prepare the Offline Plugin Bundle ​

On an internet-connected workstation, clone the repository or download the release archive:

# Clone repository onto an internet-connected machinegit clone https://github.com/routewarden/traefik-warden.git

Transfer the traefik-warden/ directory to your air-gapped host (e.g., via secure artifact repository, internal registry, USB drive, or SCP).

Step 2: Production Docker Compose Setup ​

Place the traefik-warden directory adjacent to your docker-compose.yml:

# docker-compose.yml (Air-Gapped Production via /plugins-local)services:  traefik:    image: traefik:latest    command:      - "--providers.docker=true"      - "--providers.file.filename=/etc/traefik/dynamic.yml"      - "--entrypoints.web.address=:80"      - "--entrypoints.websecure.address=:443"      # Enable offline local plugin:      - "--experimental.localplugins.routewarden.modulename=github.com/routewarden/traefik-warden"      # Attach globally to entrypoints:      - "--entrypoints.web.http.middlewares=routewarden@file"      - "--entrypoints.websecure.http.middlewares=routewarden@file"    ports:      - "80:80"      - "443:443"    volumes:      - /var/run/docker.sock:/var/run/docker.sock:ro      - ./dynamic.yml:/etc/traefik/dynamic.yml:ro      # Mount RouteWarden into required /plugins-local destination:      - ./traefik-warden:/plugins-local/src/github.com/routewarden/traefik-warden:ro

Step 3: Kubernetes Air-Gapped Deployment ​

In Kubernetes environments using the official Traefik Helm chart or direct manifests, mount the plugin source:

# Helm values.yaml for TraefikadditionalArguments:  - "--experimental.localplugins.routewarden.modulename=github.com/routewarden/traefik-warden"extraVolumes:  - name: routewarden-plugin    configMap:      name: routewarden-plugin-sourceextraVolumeMounts:  - name: routewarden-plugin    mountPath: /plugins-local/src/github.com/routewarden/traefik-warden    readOnly: true

6. Common Pitfalls & Troubleshooting ​

SymptomCauseSolution
unable to find plugin "routewarden"Incorrect container mount pathEnsure the destination path inside the container is exactly /plugins-local/src/github.com/routewarden/traefik-warden, NOT /plugins-local/traefik-warden.
permission deniedFile permission restrictionsEnsure the Traefik process has read access (chmod -R 755 traefik-warden).
missing plugin manifestMissing .traefik.yml fileVerify that .traefik.yml is present in the root of the mounted directory.
module name mismatchCLI flag does not match directoryVerify --experimental.localplugins.routewarden.modulename=github.com/routewarden/traefik-warden exactly matches the subpath /plugins-local/src/github.com/routewarden/traefik-warden.

Released under the MIT License.