Expose docker service locally and publicly

Hi everyone\
I have a couple of services (vaultwarden and homepage) that I want to access both publically via:
vaultwarden.example.com and homepage.example.com

And locally via:
vaultwarden.local.example.com and homepage.local.example.com

The .local domains are DNS entries in my pihole pointing to my local server ip running traefik

The public domains are exposed via Cloudflare tunnels and map the domain to a port on my local server.

This is my traefik docker-compose file:

version: '3'

services:
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    environment:
      - CF_API_EMAIL=REDACTED
      - CF_API_KEY=REDACTED
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /home/pi/traefik/data/traefik.yml:/traefik.yml:ro
      - /home/pi/traefik/data/acme.json:/acme.json
      - /home/pi/traefik/data/config.yml:/config.yml:ro
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`traefik.local.example.com`)"

      - "traefik.http.middlewares.traefik-auth.basicauth.users=REDACTED"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.middlewares.sslheader.headers.customrequestheaders.X-Forwarded-Proto=https"

      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.local.example.com`)"
      - "traefik.http.routers.traefik-secure.middlewares=traefik-auth"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.traefik-secure.tls.domains[0].main=local.example.com"
      - "traefik.http.routers.traefik-secure.tls.domains[0].sans=*.local.example.com"
      - "traefik.http.routers.traefik-secure.service=api@internal"

networks:
  proxy:
    external: true

This is the traefic.yml file:

accessLog: {}
log:
  level: DEBUG
api:
  dashboard: true
  debug: true
entryPoints:
  http:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: https
          scheme: https
  https:
    address: ":443"
serversTransport:
  insecureSkipVerify: true
providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false
  file:
    filename: /config.yml
certificatesResolvers:
  cloudflare:
    acme:
      email: REDACTED
      storage: /acme.json
      dnsChallenge:
        provider: cloudflare
        #disablePropagationCheck: true # uncomment this if you have issues pulling certificates through cloudflare, By setting this flag to true disables the need to wait for the propagation of the TXT record to all authoritative name servers.
        resolvers:
          - "1.1.1.1:53"
          - "1.0.0.1:53"

Setting up ´vaultwarden` works perfectly fine. If I explicitly map port 8080 to 80 I can access the service via the public and local domain. Set up like this:

version: '3'

services:
  vaultwarden:
    image: vaultwarden/server
    container_name: vaultwarden
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - "8080:80"
    volumes:
      - ./vw-data/:/data/
    environment:
      - ADMIN_TOKEN=REDACTED
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.entrypoints=http"
      - "traefik.http.routers.vaultwarden.rule=Host(`vaultwarden.local.example.dev`)"
      - "traefik.http.middlewares.vaultwarden-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.vaultwarden.middlewares=vaultwarden-https-redirect"
      - "traefik.http.routers.vaultwarden-secure.entrypoints=https"
      - "traefik.http.routers.vaultwarden-secure.rule=Host(`vaultwarden.local.example.dev`)"
      - "traefik.http.routers.vaultwarden-secure.tls=true"
      - "traefik.http.routers.vaultwarden-secure.service=vaultwarden"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=80"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

However, if I do the exact same thing setting up homepage I get a 502 error on both domains. This is the traefik log:\

time="2023-02-02T15:14:22+01:00" level=debug msg="'502 Bad Gateway' caused by: dial tcp 172.20.0.2:80: connect: connection refused"
10.0.0.5 - - [02/Feb/2023:14:14:22 +0000] "GET / HTTP/2.0" 502 11 "-" "-" 3739 "homepage-secure@docker" "http://172.20.0.2:80" 2ms
version: "3"
services:
  homepage:
    image: ghcr.io/benphelps/homepage:latest
    container_name: homepage
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - "3000:80"
    volumes:
      - /home/pi/homepage/config:/app/config # Make sure your local config directory exists
      - /var/run/docker.sock:/var/run/docker.sock:ro # (optional) For docker integrations
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.homepage.entrypoints=http"
      - "traefik.http.routers.homepage.rule=Host(`homepage.local.example.dev`)"
      - "traefik.http.middlewares.homepage-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.homepage.middlewares=homepage-https-redirect"
      - "traefik.http.routers.homepage-secure.entrypoints=https"
      - "traefik.http.routers.homepage-secure.rule=Host(`homepage.local.example.dev`)"
      - "traefik.http.routers.homepage-secure.tls=true"
      - "traefik.http.routers.homepage-secure.service=homepage"
      - "traefik.http.services.homepage.loadbalancer.server.port=80"
      - "traefik.docker.network=proxy"
networks:
  proxy:
    external: true

I know the container works because different ip configs let me access the service via one or the other domains.

# Neither domains work
ports:
  - "3000:80"
labels:
  - "traefik.http.services.homepage.loadbalancer.server.port=80"

# Only works with local domain
ports:
  - "3000:80"
labels:
  - "traefik.http.services.homepage.loadbalancer.server.port=3000"

# Also only works with local domain (no port mapping specified)
labels:
  - "traefik.http.services.homepage.loadbalancer.server.port=3000"

# Only works with public domain
ports:
  - "3000:3000"
labels:
  - "traefik.http.services.homepage.loadbalancer.server.port=3000"

# Also only works with public domain
ports:
  - "3000:3000"
labels:
  - "traefik.http.services.homepage.loadbalancer.server.port=80"

Does anyone have a clue why It does not work when I set it up like my vaultwarden service.
(I am not running anything else on port 3000 and 8080)

Thanks in advance

Do you want both external and internal traffic go through Traefik? Why not just use both domains in the router? No need to expose the ports of the target containers.

.rule=Host(`internal`) || Host(`external`)

PS: You can use a single central http->https redirect in http entrypoint.

1 Like

Thanks for the response

I tried to apply your suggestions to the vaultwarden config

version: '3'

services:
  vaultwarden:
    image: vaultwarden/server
    container_name: vaultwarden
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    volumes:
      - ./vw-data/:/data/
    environment:
      - ADMIN_TOKEN=REDACTED
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.entrypoints=http"
      - "traefik.http.routers.vaultwarden.rule=Host(`vaultwarden.local.example.dev`) || Host(`vaultwarden.example.dev`)"
      - "traefik.http.middlewares.vaultwarden-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.vaultwarden.middlewares=vaultwarden-https-redirect"
      - "traefik.http.routers.vaultwarden-secure.entrypoints=https"
      - "traefik.http.routers.vaultwarden-secure.rule=Host(`vaultwarden.local.example.dev`) || Host(`vaultwarden.example.dev`)"
      - "traefik.http.routers.vaultwarden-secure.tls=true"
      - "traefik.http.routers.vaultwarden-secure.service=vaultwarden"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=8080"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

I now get a 502 error when trying to access both domains

10.0.0.5 - - [02/Feb/2023:18:51:48 +0000] "GET / HTTP/2.0" 502 11 "-" "-" 5659 "vaultwarden-secure@docker" "http://172.20.0.5:8080" 2ms
time="2023-02-02T19:51:48+01:00" level=debug msg="'502 Bad Gateway' caused by: dial tcp 172.20.0.5:8080: connect: connection refused"

I only made changes to the vaultwarden file. Should I make changes elsewhere?
Thanks again.

Just doing this (not exposing ports and sticking to the local domain only) is inaccessible locally (and publicly)

version: '3'

services:
  vaultwarden:
    image: vaultwarden/server
    container_name: vaultwarden
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    volumes:
      - ./vw-data/:/data/
    environment:
      - ADMIN_TOKEN=REDACTED
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.entrypoints=http"
      - "traefik.http.routers.vaultwarden.rule=Host(`vaultwarden.local.example.dev`)"
      - "traefik.http.middlewares.vaultwarden-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.vaultwarden.middlewares=vaultwarden-https-redirect"
      - "traefik.http.routers.vaultwarden-secure.entrypoints=https"
      - "traefik.http.routers.vaultwarden-secure.rule=Host(`vaultwarden.local.example.dev`)"
      - "traefik.http.routers.vaultwarden-secure.tls=true"
      - "traefik.http.routers.vaultwarden-secure.service=vaultwarden"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=8080"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

But portainer works fine when not exposing any ports (only locally ofc)?

version: '3'

services:
  portainer:
    image: portainer/portainer-ce
    container_name: portainer
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /home/pi/portainer/data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.entrypoints=http"
      - "traefik.http.routers.portainer.rule=Host(`portainer.local.example.dev`)"
      - "traefik.http.middlewares.portainer-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.portainer.middlewares=portainer-https-redirect"
      - "traefik.http.routers.portainer-secure.entrypoints=https"
      - "traefik.http.routers.portainer-secure.rule=Host(`portainer.local.example.dev`)"
      - "traefik.http.routers.portainer-secure.tls=true"
      - "traefik.http.routers.portainer-secure.service=portainer"
      - "traefik.http.services.portainer.loadbalancer.server.port=9000"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

Is your local DNS pointing your local domain to the Traefik server IP?

1 Like

Yes my server which is running traefik has the ip 10.0.0.9

Below image show my list of local DNS domains in pihole:

And as you can see portainer works fine with the local DNS:

And the config as showed before:

version: '3'

services:
  portainer:
    image: portainer/portainer-ce
    container_name: portainer
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /home/pi/portainer/data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.entrypoints=http"
      - "traefik.http.routers.portainer.rule=Host(`portainer.local.example.com`)"
      - "traefik.http.middlewares.portainer-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.portainer.middlewares=portainer-https-redirect"
      - "traefik.http.routers.portainer-secure.entrypoints=https"
      - "traefik.http.routers.portainer-secure.rule=Host(`portainer.local.example.com`)"
      - "traefik.http.routers.portainer-secure.tls=true"
      - "traefik.http.routers.portainer-secure.service=portainer"
      - "traefik.http.services.portainer.loadbalancer.server.port=9000"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

While vaultwarden now does not work:

Also with the config showed before very similar to the portainer one:

version: '3'

services:
  vaultwarden:
    image: vaultwarden/server
    container_name: vaultwarden
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    volumes:
      - ./vw-data/:/data/
    environment:
      - ADMIN_TOKEN=REDACTED
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.vaultwarden.entrypoints=http"
      - "traefik.http.routers.vaultwarden.rule=Host(`vaultwarden.local.example.com`)"
      - "traefik.http.middlewares.vaultwarden-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.vaultwarden.middlewares=vaultwarden-https-redirect"
      - "traefik.http.routers.vaultwarden-secure.entrypoints=https"
      - "traefik.http.routers.vaultwarden-secure.rule=Host(`vaultwarden.local.example.com`)"
      - "traefik.http.routers.vaultwarden-secure.tls=true"
      - "traefik.http.routers.vaultwarden-secure.service=vaultwarden"
      - "traefik.http.services.vaultwarden.loadbalancer.server.port=8080"
      - "traefik.docker.network=proxy"

networks:
  proxy:
    external: true

Did you check the vaultwarden proxy docs?

@williamwoldum I'm looking to do exactly the same thing.
Have you been able to solve your problems?
If so, it would be great if you'd share your results.