Isolating Internal and External Ingress with Traefik, Docker, VLANs, and Provider Constraints

In modern homelab and production environments, it’s often necessary to separate internal (private) and external (public) ingress traffic for security, compliance, and operational clarity. With Docker, Traefik, and VLANs, you can achieve this cleanly—without exposing sensitive services to the wrong network.

Below is a practical, production-grade approach using:

  • Docker Compose
  • Traefik (multiple instances)
  • VLAN-backed Docker networks
  • Provider constraints for service discovery

1. Network Topology: VLAN-backed Docker Networks

Create two Docker networks, each mapped to a different VLAN/subnet:

networks:
  trf_ext:
    driver: ipvlan
    driver_opts:
      parent: eth0.1125
      ipvlan_mode: l2
    ipam:
      config:
        - subnet: 10.2.125.0/24
          gateway: 10.2.125.1

  trf_int:
    driver: ipvlan
    driver_opts:
      parent: eth0.1130
      ipvlan_mode: l2
    ipam:
      config:
        - subnet: 10.2.130.0/24
          gateway: 10.2.130.1
  • trf_ext is for external/public traffic (VLAN 1125).
  • trf_int is for internal/private traffic (VLAN 1130).

2. Multiple Traefik Instances

Run two Traefik containers, each on its own VLAN-backed network:

External Traefik (traefik-ext.yaml)

services:
  traefik-external:
    image: traefik:latest
    networks:
      trf_ext:
        ipv4_address: 10.2.125.254
      socket_proxy:
    command:
      - --providers.docker=true
      - --providers.docker.network=trf_ext
      - --providers.docker.exposedByDefault=false
      - --providers.docker.constraints=Label(`traefik.group`, `external`)
      # ...other flags...

Internal Traefik (traefik-int.yaml)

services:
  traefik-internal:
    image: traefik:latest
    networks:
      trf_int:
        ipv4_address: 10.2.130.254
      socket_proxy:
    command:
      - --providers.docker=true
      - --providers.docker.network=trf_int
      - --providers.docker.exposedByDefault=false
      - --providers.docker.constraints=Label(`traefik.group`, `internal`)
      # ...other flags...

3. Provider Constraints for Service Discovery

Provider constraints ensure each Traefik instance only discovers and routes the services you intend.

  • Add a label to each service:
    • traefik.group=external for public services
    • traefik.group=internal for private services

Example:

services:
  ghost:
    image: ghost
    networks:
      - trf_ext
    labels:
      - "traefik.enable=true"
      - "traefik.group=external"
      # ...other traefik labels...

  portainer:
    image: portainer/portainer-ce
    networks:
      - trf_int
    labels:
      - "traefik.enable=true"
      - "traefik.group=internal"
      # ...other traefik labels...

4. TLS and Certificate Resolvers

Each Traefik instance can use its own certificate resolver (e.g., Let’s Encrypt for external, ZeroSSL for internal):

# traefik-ext.yaml
- --entrypoints.websecure.http.tls.certresolver=dns-cloudflare

# traefik-int.yaml
- --entrypoints.websecure.http.tls.certresolver=zerossl

5. Result: Clean, Isolated Ingress

  • External Traefik only sees and routes services labeled traefik.group=external and attached to trf_ext.
  • Internal Traefik only sees and routes services labeled traefik.group=internal and attached to trf_int.
  • No accidental exposure of internal services to the public.
  • Each VLAN/subnet is isolated at L2, and only the intended services are reachable.

6. Extra: Fine-Grained Control

You can use more complex constraints for advanced scenarios:

- --providers.docker.constraints=Label(`traefik.group`, `internal`) && Label(`traefik.env`, `prod`)

Or use regex:

- --providers.docker.constraints=LabelRegex(`traefik.group`, `int.*`)

Conclusion

By combining Docker’s network isolation, Traefik’s multi-instance support, and provider constraints, you can build a robust, secure, and maintainable ingress architecture for any environment—homelab or production.

No sensitive data is exposed, and you have full control over what’s public and what’s private.


References:


Happy homelabbing!