Make Traefik Load-balance Docker and External Services

I spent some time trying to figure it out, but eventually cracked the problem. Below is an logical diagram of what I wanted.

Logical layout of what I want to do

Spoiler alert: You cannot do everything with Docker labels

I really like Traefik's way on configuring services with docker labels. I tried a number of different ways using labels to define services external to Docker and ask Traefik to route there.

No luck.

In the end the only way I was able to make it work was to add the definition to an external file, at which point everything worked smoothly.

Show me the config!

Right, so I guess you want to see some config code. This is my traefik_conf.yml which is mounted into the Traefik container.

In this particular case we are proxying a URI off my main domain to a new service. In this case, https://www.simplecto.com/mynewservice will be proxied to an externally hosted service on http://10.10.100.200:8080.

It also uses the stripprefix middleware.

I prefer this case because it minimizes the number of subdomains to manage in DNS.

traefik_conf.yml

http:
  routers:
    mynewservice:
      service: mynewservice
      middlewares:
        - "mynewservice-stripprefix@file"
      rule: "Host(`www.simplecto.com`) && PathPrefix(`/mynewservice`)"
      tls:
        certResolver: le

  services:
    mynewservice:
      loadBalancer:
        servers:
          - url: "http://10.10.100.200:8080"

  middlewares:
    mynewservice-stripprefix:
      stripPrefix:
        prefixes:
          - "/mynewservice"

this is a part of my docker-compose.yml that configures traefik with command line options.

version: '3'

services:

    traefik:
        container_name: traefik
        image: traefik:v2.1
        command:
            - "--api.insecure=true"
            - "--providers.docker=true"
            - "--entryPoints.web.address=:80"
            - "--entryPoints.websecure.address=:443"
            - "--certificatesResolvers.le.acme.email=email@domain.com"
            - "--certificatesResolvers.le.acme.storage=acme.json"
            - "--certificatesResolvers.le.acme.tlsChallenge=true"
            - "--certificatesResolvers.le.acme.httpChallenge=true"
            - "--certificatesResolvers.le.acme.httpChallenge.entryPoint=web"
            - "--providers.file.filename=/traefik_conf.yaml"
            - "--accesslog=true"
            - "--accesslog.filepath=/logs/access.log"
            - "--accesslog.bufferingsize=100"
        restart: always
        ports:
            - 80:80
            - 443:443
            - 8080:8080
        networks:
            - traefik
        volumes:
            - /var/run/docker.sock:/var/run/docker.sock:ro
            - ./acme.json:/acme.json
            - ./traefik_conf.yaml:/traefik_conf.yaml:ro
            - ./logs:/logs
        labels:

Pay particular attention to this line.

        - "--providers.file.filename=/traefik_conf.yaml"

This tells docker to pull file configs.

Also, notice in the volumes: section where we bind-mount the config file to the container.

Conclusion

Honestly, this was more for me than it was for you. I put up many of these posts just so that I don't forget. Hopefullly it helps you as well.

Feel free to email me or reach out for any comments / suggestions to improve this arrangement.