Forwarding UDP to a container without running in host network mode

This is a cross-post from StackOverflow.

Summary

I have network connected devices (IoT devices) responding to a UDP broadcast initiated by my containerised app. The app sends this to port 30303 of the container, but the actual port that this gets sent on the host network changes every time the app is started with docker-compose, so my app never recieves the response. See the annotated image below that depicts this behaviour.

How can I either:

  • Ensure that my UDP packets sent on the container port get sent on the same port on the host (in this instance, ensuring that the packets are sent on port 30303 of both the host and container); or

  • Ensure that my UDP packets are always sent from the same host port, so I know which host port to forward to my application container

More information
The container is on a bridge network (it needs to communicate with 2 other containers in this setup) so I cannot resort to host mode networking. Achieving the UDP broadcast to occur on the host network without running the application in host networking mode was not straightforward to achieve with Docker’s native functionality, I used this script to allow the proper forwarding to occur.

I have bound the ports in the docker-compose.yml file and exposed them in the build file.

I would prefer if I could achieve this with native Docker functionality (i.e. not resort to writing a host-level application that forwards the packets).

docker-compose.yml

# This file is for x86-64 builds
version: "3.8"
services:
  api-server:
    image: myserver
    ports:
      - 5000:5000
      - 30303:30303/udp
    expose:
      - "30303"
    networks:
      - my-network
    depends_on:
      - mongo
      - redis
    restart: always
    environment: 
      - NODE_ENV=production
    build:
      context: .
      dockerfile: ./Dockerfile
  client:
    image: myclient
    ports:
      - 3000:80
    restart: always
    environment: 
      - NODE_ENV=production
    depends_on: 
      - api-server
    build:
      context: ./client
      dockerfile: ./Dockerfile
  mongo:
    image: mongo:3.6.19-xenial
    ports:
      - 27017:27017
    networks:
      - my-network
    volumes:
      - mongo-data:/data/db
    restart: always
  redis:
    image: redis:alpine
    ports:
      - 6379:6379
    networks:
      - my-network
    restart: always
networks:
  my-network:
    driver: bridge
volumes:
  mongo-data:
    driver: local

Dockerfile

FROM node:14

# Create app directory
WORKDIR /usr/src/app

COPY package*.json ./

RUN npm install

# Bundle app source
COPY . .
RUN npm install -g typescript
RUN npm run copy-public
RUN tsc

EXPOSE 5000
EXPOSE 30303
CMD [ "node", "./dist/server/src/index.js" ]