Issue with Exposing a Range of Ports in Docker Compose

Hi there! :grin:

As mentioned in the Docker documentation, I’m trying to expose a range of around 300 ports. However, the result I get is just 0/tcp.

Is this a bug, or am I doing something wrong? I’ve been racking my brain over this :face_with_head_bandage:.

I’ve tried multiple configurations, but I keep getting the same result.

Any help would be greatly appreciated!

Thanks!

Docker Compose version v2.29.1

On Debian or windows

Compose File

services:
  hello_world:
    image: debian
    expose:
     - 58787-59000
    command:
      - /bin/bash
      - -c
      - |
         while [ 1 ]; do echo " hello world !" ;sleep 30 ;done;

When i run

sudo docker compose up -d --build 

EXPECTED

debian                             "/bin/bash -c 'while…"   3 minutes ago    Up 3 minutes                    58787-5900/tcp
...
        "Config": {
            "Hostname": "3f9dea0b4aa7",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": true,
            "AttachStderr": true,
            "ExposedPorts": {
                "58787-59000": {}
…
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "*****************************",
            "SandboxKey": "/var/run/docker/netns/**************",
            "Ports": {
                "58787-59000/tcp"

RESULT

   debian                             "/bin/bash -c 'while…"   7 seconds ago    Up 6 seconds                    0/tcp
...
        "Config": {
            "Hostname": "3f9dea0b4aa7",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": true,
            "AttachStderr": true,
            "ExposedPorts": {
                "58787-59000": {}
…
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "*****************************",
            "SandboxKey": "/var/run/docker/netns/**************",
            "Ports": {
                "0/tcp": null

If you want to publish container ports to host ports, you need to use ports.

services:
  hello_world:
    image: debian
    ports:
     - "58787-59000:58787-59000"
    command:
      - /bin/bash
      - -c
      - |
         while [ 1 ]; do echo " hello world !" ;sleep 30 ;done;

See: https://docs.docker.com/reference/compose-file/services/#ports

Hi Meyay,

Thank you for the quick response.

I don’t want to map the port on the host; I just need to open it within the Docker Compose stack.

After extensive testing, I found a way to achieve this. I realized that it only works on the Dockerfile side.

:rocket: WORKS!

docker-compose.yml

services:
  hello_world:
    container_name: "hello_world"
    build:
      context: .
      args:
        - PORTS=58787-59000

Dockerfile

FROM debian:latest

ARG PORTS

COPY ./init.sh /etc/init.sh

EXPOSE $PORTS

CMD ["/etc/init.sh"]

init.sh

#!/bin/bash
while [ 1 ]; do
    echo "Hello, world!";
    sleep 30;
done;

:no_entry_sign: DOESN’T WORK IN THE DOCKER COMPOSE AT ALL

docker-compose.yml


services:
  hello_world:
    container_name: "hello_world"
    build:
      context: .
    expose:
      - "${PORTS}"
    environment:
      - PORTS=58787-59000

Dockerfile

FROM debian:latest

COPY ./init.sh /etc/init.sh

CMD ["/etc/init.sh"]

It doesn’t work on the Compose side, only on the Dockerfile side, and I’m not sure why.

I assume the ARG directive works because it passes the argument to the Dockerfile before it’s built. My understanding is that ENV simply passes variables to Docker Compose, allowing me to pass variables from a .ENV file to the ARG in the Docker Compose file, which should complete the job.

Ports are always open between containers in a stack. That EXPOSE is just simply a metadata for load balancers and other services that automatically detects where to forward requests.

As for the original question, although you don’t need the expose, the only difference I see is that the documentation uses quotation marks and you didn’t.

Dockerfile does not support port ranges.

https://docs.docker.com/reference/dockerfile/#expose

Hi Rimelek,

I hoped that using quotation marks would solve my problem, but unfortunately, it didn’t.

I understand that exposing ports inside the container is unnecessary for the container itself. However, I pass the ports to my service during its construction because I want it to advertise the port it is listening on.

I hope that’s clear.

I see. Then it is not to open ports after all. I tested your port range which didn’t work and I tested 3000-4000 which worked so I guess the chosen port range is not supported. I’m not sure why

Well, if “EXPOSE” is not intended for exposing ports, then the term seems inappropriate.
They should consider using a term like “ROUTING_PORT” or something more suitable. :neutral_face:
But who cares ? maybe cysco ingenior :stuck_out_tongue_winking_eye:

Additionally, using "PORT" works but automatically maps a random port on the host, which is not ideal.

Port range 3000-4000 directly in the Compose file doesn’t work either for me.

services:
  hello_world:
    container_name: "hello_world"
    build:
      context: .
    expose:
      - 3000-4000 

It still shows: 0/TCP.

The only workaround I’ve found is to modify the Dockerfile, which isn’t ideal if I want to use a pre-built Docker Hub image directly in the Docker Compose file. I haven’t even tested if it’s truly “opening/routing” the port correctly.

I personally don’t care much as I almost never use it when I can set the port in a label for a load balancer, but yes, we are saying it for a long time on the forum :slight_smile: just haven’t proposed a change yet. Mentioning it more clearly in the documentation could help too.

For me too… at least in the command line when listing containers, but not the container inspect output which I checked last time. So Docker knows about the metadata.

docker container inspect hello_world --format '{{ json .Config.ExposedPorts }}'

Output:

{"58787-59000/tcp":{},"80/tcp":{}}

So yur services should be able to detect the port range, the CLi just has a bug apparently. I’m not sure why I did not see it when I first tested your port range. I remembered to see 0/tcp in the container inspect output, but I was either wrong or my original compose file (before your hello world short example) worked differently. I tried to restore the original file, although not perfectly, but I still see the right port.

I am facing the same issue, any clues on how to fix this

The same here in Docker compose, in Docker CLI itself and in Portainer so i think its a ignored Docker Problem with many closed Issues on Github.

I need a port Range for TVHeadend from 30000-65000/udp

Docker Compose hangs up, Portainer hangs up and Docker CLI hangs up.

Docker compose:

 ports:
      - "30000-65000:30000-65000/udp"

Docker CLI:

docker run -d … -p 30000-65000:30000:65000/udp …

A client (every tool you listed is just a client) sends an api request to the docker engine, which tries to create the container with desired configuration.

While doing so, it will run a command like this for every (!) port individually:

/usr/sbin/iptables --wait -t nat -C DOCKER -p tcp -d 0/0 --dport <host port> -j DNAT --to-destination <container ip:container port> -i docker0

With n ports in a range, you get n times the execution time delay. The bigger the range, the longer the waiting time.

I can’t imagine that TV Headed actually requires 35000 udp ports, or does not allow configuring the range. You are probably fine with a fraction of udp ports. Try configuring a range of 100, it shouldn’t delay start of the container noticeably.

Docker doesn’t hang. It just needs way more time to create the container with the huger number of ports you made it forward from the host to the container.

I doubt that its necessary to handle every port individually, as iptables should be able to handle forwarding a range as well.

2 Likes

So i can change the iptables manually, but why Docker needs so long to add this udp ports ?

TVheadend needs this udp ports for SATIP and i cant change it. TVHeadend searches every Reboot for the SATIP Server (in the lokal Network) and its uses every Time another UDP Port between this Range. Its crappy i know but what should i do ?

You could. My point was more along the line that it would be worth opening an issue in the Moby project and ask and request a change when ranges are published, instead of single ports. It could be very well a single iptables rule that covers all ports. iptables seem to take that long, and docker waits for iptables to apply all 35k rules.

Did you try to ask in a TVHeadend related forum whether there is a way? If I google for the terns “tvheadend configure sat ip udp range” there is an ai generated overview, that might be worth trying. I am not going to post it here, because it is not allowed to post ai generated content in this forum.

From a Docker tvheadend project:

Host vs. Bridge

If you use IPTV, SAT>IP or HDHomeRun, you need to create the container with --net=host and remove the -p flags.

Source

1 Like

It works now fine thanks again

@weeziboum

Exposing a range of 300 ports in Docker Compose often leads to a 0/tcp result. This is not typically a bug. It usually indicates a misconfiguration in how Docker handles port ranges.

Docker Compose has limitations when defining large, contiguous port ranges directly in the ports section. While it supports ranges like 8000-8010:8000-8010, a range of 300 ports can overwhelm the parsing or allocation mechanism.

For setups involving IPTV, XMLTV grabbers, EPG grabbers, or TVHeadend, you often need many ports. Instead of exposing a large range, consider network modes or individual port mappings.

One approach is to map specific ports required by your services. For example, explicitly map critical ports like 9981 and 9982 for TVHeadend.

Another method involves using host networking mode. When you use network_mode: “host” in your Docker Compose file, the container directly uses the host’s network stack. This bypasses Docker’s port mapping entirely. All ports opened by the container are then directly accessible on the host. This can be simpler for applications requiring many ports, but it removes network isolation.

If you must use a bridged network and many ports, use individual port entries rather than large ranges. This makes the configuration explicit for each port needed.