Reverse proxy minecraft server

Hi, I just want to reverse proxy my minecraft server so that when I connect to my external IP on port 25565, traefik forwards my packets to an internal ip address at port 25565

Do you need TLS on the port? Will the Minecraft server create a certificate?

From my research, Minecraft uses a version of TCP that doesn't support TLS. So no.

Any ideas on how to achieve this?

Create a Traefik container listening on your port, create a dynamic config file to proxy/forward any incoming connection on that entrypoint to a target service.

minecraft Docker service is a fake inside Docker network. But you can just adjust the target IP:port in the dynamic config file, even to outside Docker network.

docker-compose.yml:

services:
  traefik:
    image: traefik:v3
    volumes:
      - ./traefik-dynamic.yml:/traefik-dynamic.yml
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-certificates:/certificates
    ports:
      - 80:80
      - 443:443
      - 25565:25565
    networks:
      - proxy
    command:
      - --providers.docker=true
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=proxy
      - --providers.file.filename=/traefik-dynamic.yml
      - --providers.file.watch=true
      - --entryPoints.web.address=:80
      - --entryPoints.web.http.redirections.entryPoint.to=websecure
      - --entryPoints.web.http.redirections.entryPoint.scheme=https
      - --entryPoints.websecure.address=:443
      - --entryPoints.websecure.http.tls.certResolver=myresolver
      - --entryPoints.minecraft.address=:25565
      - --api.debug=true
      - --api.dashboard=true
      - --log.level=INFO
      - --accesslog=true
      - --certificatesResolvers.myresolver.acme.email=mail@example.com
      - --certificatesResolvers.myresolver.acme.storage=/certificates/acme.json
      - --certificatesresolvers.myresolver.acme.tlschallenge=true
    labels:
      - traefik.enable=true
      - traefik.http.routers.api.entrypoints=websecure
      - traefik.http.routers.api.rule=Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))
      - traefik.http.routers.api.service=api@internal
      - traefik.http.routers.api.middlewares=auth
      - 'traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/'

  whoami:
    image: traefik/whoami:v1.10
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.whoami.entrypoints=websecure
      - traefik.http.routers.whoami.rule=Host(`whoami.example.com`)
      - traefik.http.services.whoami.loadbalancer.server.port=80

  minecraft:
    image: traefik/whoami:v1.10
    networks:
      - proxy

volumes:
  traefik-certificates:

networks:
  proxy:
    name: proxy
    attachable: true

traefik-dynamic.yml:

tcp:
  routers:
    forward:
      entrypoints:
        - minecraft
      rule: HostSNI(`*`)
      service: forward

  services:
    forward:
      loadBalancer:
        servers:
          - address: minecraft:80