Traefik `Path` working for one container but not another (+ Cloudflare Tunnel)

Literally what is described in the title, I configured CF Tunnel + Traefik, so that I create a single hostname in CF like app.mydomain.com, and in Traefik I created a router with a Path rule for two containers: whoami and portainer. Whoami works 100%, but Portainer gives me 404 page not found by Traefik, look at my configs:

Docker compose:

networks:
  selfhost:
    external: true

services:
  cloudflare_tunnel:
    container_name: cloudflare_tunnel
    image: cloudflare/cloudflared:1681-ac57ed970926
    restart: unless-stopped
    command: tunnel --no-autoupdate run
    environment:
        TUNNEL_TOKEN: mytoken
    networks:
        - selfhost
  portainer:
    image: portainer/portainer-ce:2.21.5
    container_name: portainer
    networks:
      - selfhost
    volumes:
      - ./portainer:/data
      - /var/run/docker.sock:/var/run/docker.sock
    restart: unless-stopped
  whoami:
    image: traefik/whoami:v1.10
    container_name: whoami
    networks:
      - selfhost
    restart: unless-stopped
  traefik:
    image: traefik:v3.2
    container_name: traefik
    networks:
      - selfhost
    volumes:
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./config:/config:ro
    restart: unless-stopped

Static Traefik.yml:

entryPoints:
  web:
    address: :80

api:
  insecure: true

providers:
  file:
    directory: /config
    watch: true

log:
  level: DEBUG

My config/dynamic.yml:

http:
  routers:
    whoami:
      rule: Path(`/whoami`)
      service: whoami
      entryPoints:
        - web

    portainer:
      rule: Path(`/portainer`)
      service: portainer
      entryPoints:
        - web

  services:
    whoami:
      loadBalancer:
        servers:
          - url: http://whoami:80

    portainer:
      loadBalancer:
        servers:
          - url: http://portainer:9000

On Cloudflare tunnel hostnames, this is what I configured:
whoami.mydomain.com -> http://whoami:80
portainer.mydomain.com -> http://portainer:9000
traefik.mydomain.com -> http://traefik:8080
app.mydomain.com -> http://traefik:80

I don't need to bind the container ports, since CF Tunnel have access to the containers through the docker network DNS, so I can just use the containers' internal ports directly.

Current behavior:
All the three first hostnames work: whoami., portainer. and traefik., but when it comes to the last one app., I type in the browser app.mydomain.dev/whoami and it works, but if I type instead app.mydomain.com/portainer, Traefik gives me 404 page not found.

What am I missing here? You can see that portainer is working with its own subdomain, but not for path!

PS.: I just added a new router and service for Traefik like below, and I got the SAME error as Portainer... I even tested reordering the routers in the dynamic.yml file, but only WHOAMI works at all.

http:
  routers:
    # ...
    dashboard:
      rule: Path(`/traefik`)
      service: dashboard
      entryPoints:
        - web

  services:
    # ...
    dashboard:
      loadBalancer:
        servers:
          - url: http://traefik:8080

Why would you expect that Portainer service is responding to path /portainer?

Usually GUI web apps expect to be root (/), you can only use a custom path when you can configure some kind of "base path" in the app. Because of this, sub-domains are best practice, used with Host() rule.

I still dont understand one thing: if it's just best practice, then it should still work, correct?

I understand it being better using a subdomain, but the thing is that for this particular case I would like CF tunnel to just point to my Traefik reverse proxy, then let Traefik handle all the redirects. Since CF certificates do not cover subdomain of subdomains, and I want only a single subdomain pointing to Traefik, I cant do like portainer.app.mydomain.com, right?

To start, I am not sure if using Cloudflare is best practice, as they can read all the traffic going through their system, not sure if that is desireable.

You created this setup:

I would assume that CF keeps host and path in the http requests intact. So local whoami should point to Traefik. If you have CF inside Docker network, all domains should point to traefik. Then you should use something like

rule: Host(`whoami.mydomain.com`)

inside Traefik dynamic config to match the request.

I understand you being concerned about CF reading the whole traffic, but CF tunnels are interesting in some scenarios, like the one I mentioned in the other post about using ports 8000 and 4433! But that's not the case, I really would like to learn how to best integrate these stuffs!

As I mentioned, whoami.mydomain.com works, so does app.mydomain.com/whoami, but why would portainer.mydomain.com work and app.mydomain.com/portainer and /traefik doesn't?

I understood the best practice of using a subdomain for a GUI application, but let's think of a scenario where no new subdomains are available, shouldn't Traefik serve it as I need?

Portainer service does not use /portainer, it's just not a valid path for the application. And Traefik uses /dashboard/, but also needs /api.

The original request paths are passed to the target services.

what do you mean by Portainer do not use /portainer? I mean, isn't the dev (me) configuring Traefik what hosts and what paths send the user to whichever application running I want? I should be able to tell Traefik that I do want it to serve portainer using that path, right?

Yes, Traefik will just proxy/forward the request.

But Portainer doesn’t know what to do with a request to path /portainer. It expects an initial request to /.

When you publish the port of Portainer directly to host and send the request to it, it will also respond with 404 for /portainer, without Traefik.

That doesn't make total sense in my head... I mean, how does portainer know I am using /portainer? Or even /traefik for the Traefik dashboard? And why does it expect a subdomain instead?

When I connect to app.mydomain.com/portainer, in my head what happens is that Traefik is redirecting the traffic to the portainer container on port 9000, but no information about the hostname and path would be shared to the application Portainer itself, since it shouldn't matter for it, right?

It seems you never created a web app :wink:

Functionality usually depends on GET and POST requests and a path, like /, login and logout. The app will not react to unknown paths.

The app usually doesn’t care about host. Except for WordPress :sweat_smile:

And of course the host and path is forwarded to the app by Traefik, how else should it work?

actually I did :laughing: but I really thought apps would kind of ignore the part of the host used to access the reverse proxy, if you know what I mean...

But that's fine, if it is an expected behavior, let's go with subdomains only hahaha

Thanks!!

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.