Traefik/Docker port detection/selection with bridge network and random external port

In my particular case I am using a docker-compose stack that contains Grafana, Influx, and Telegraf - but I think this issue can easily be reproduced with a single container. For experience level, I'm doing this just as a learning exercise on a home server. I'm not an expert by any means so I'm sure there's a lot I don't know here.

Let's say I have a generic container which I place on a docker bridge network. I can specify only the internal port and allow docker to pick a random external port number to avoid conflicts on the host. For example:

services:
  mycontainer:
    container_name: mycontainer
    ports:
        - "8086"
    ...
    labels:
        - "traefik.http.routers.mycontainer.rule=Host(`mycontainer.mydomain.tld`)

I can then use docker-compose port mycontainer 8086 once the container is started to find that it has mapped 8086 in the container to $randomport on the host (which most likely will not be consistent if the container is restarted). What I want is Traefik to pick up $randomport for routing so I can just navigate to mycontainer.mydomain.tld and reach $randomport on my container which in turn reaches my container's service. What I find by looking at the traefik dashboard is that Traefik picks up port 8086 as the container's exposed port (so the traffic is routed to $container_ip:8086), which obviously won't work as the external port is something else entirely.

I know that this will work if I were to specify an external port explicitly, e.g port: - "45000:8086". I could also just use the server.port label to get the appropriate resolution if automatic detection didn't work and I knew the external port. All that said, it seems like the "right" way is to allow for the randomly assigned external ports as it lessens the risk of port conflicts and increases portability, doesn't it? I know that this is a pretty unlikely situation in my particular case since it's just a computer in my house with just me using it, but I'm curious to learn "best practices" as if this were a production server with hundreds of services and dozens of users making changes.

Is there a way to get Traefik to correctly identify this random port? Or am I totally barking up the wrong tree and the "correct" solution is to just hard-code a port and keep a list to make sure other services don't try to fight with it?

There should be a simple way for a container to detect the portmappings assigned to it. (from inside the container)

Why

There are a variety of cases where an application needs to know the real external IP address and port at which it can be reached. Some examples:

  • torrent client
  • FTP server (passive mode)
  • TeamCity build agent
  • others

The external IP can be detected reliably through the use of an intermediary. However the port mappings cannot be reliably automatically detected.

Why don't you use non-dynamic ports?

The use of static ports (e.g. docker run -p 1234:1234 syntax), plus hardcoding the same portmappings into the image, allows the container to know what its port mappings are without dynamic discovery.

However this solution does not allow you to run the same image in multiple containers on the same host (as the ports would conflict), which is an important usecase for some images. It also assumes that the ports baked into the image will never be used by any other docker image that a user is likely to install, which is not a very good assumption.

Why don't you use the REST API?

Allowing a container to have access to the REST API is problematic. For one thing, the REST API is read/write, and if all you need is to read your portmappings, that's a dangerous level of permissions to grant a container just to find out a few ports.

If I understand your reply correctly, it sounds like I'm on the right general track in using random docker-assigned external ports as there is always a chance of either two images wanting the same ports, or using multiple copies of the same image.

It seems that Traefik doesn't deal with this particular scenario well? All of the documentation I've found seems to assume that the external port is known prior to container launch, so you can either explicitly set it in the container's run command/compose file, or as a label to the container. My experience is that by only specifying the internal port (so that docker randomly assigns the host port) that Traefik seems to assume the external one is the same as the internal port.

Is this intended behavior, or perhaps is this a bug? I'm picturing a scenario where I've got several services that want to be accessed on (for example) port 80 - a bunch of stuff with web consoles or something to that effect. I can either map each service to 8000, 8001, 8002, and so on on the host and just keep a list, or I let docker pick the host port at random. In the latter case is there a way to get Traefik to find out what that random port was for each service so that it can route to it? My attempts so far haven't worked, so I'm wondering if I'm doing something I shouldn't be or not doing something I should.