Running Satisfactory game server behind traefik v2

Hello everyone,

I want to run the following Satisfactory dedicated game server behind traefik v2: GitHub - wolveix/satisfactory-server: A Dockerized version of the Satisfactory dedicated server

Since I am new to traefik I am having problems setting everything up.
I configured the entrypoints and 3x udp router and 3x udp service for the game server docker container, but I was not able to connect to the game server through traefik, if i directly expose the ports on my VPS I can connect without any problems.

I think I am missing some details, could anyone please help me out with some example configuration for the linked docker container? Thanks in advance.

If you use Traefik, please post your static (entrypoints, etc.) and dynamic (routers, services, etc.) config. And your full docker-compose.yml.

@bluepuma77 sorry, thanks for mentioning, here are my configuration files:

version: '3'

services:

  traefik:
    image: traefik:v2.9
    container_name: 'traefik'
    hostname: 'traefik'
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
      - "20777:20777/udp"
      - "20000:20000/udp"
      - "9898:9898/udp"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - TRAEFIK_API_INSECURE=true
      - TRAEFIK_PROVIDERS_DOCKER=true
      - TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=:80
      - TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS=:443
      - TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT=false
      # UDP
      - TRAEFIK_ENTRYPOINTS_sf01_ADDRESS=:20777/udp
      - TRAEFIK_ENTRYPOINTS_sf02_ADDRESS=:20000/udp
      - TRAEFIK_ENTRYPOINTS_sf03_ADDRESS=:9898/udp

and

version: '3'

services:
    satisfactory-server:
        container_name: 'sf0'
        hostname: 'sf0'
        image: 'wolveix/satisfactory-server:latest'
        labels:
            - "traefik.enable=true"
            - "traefik.udp.routers.rsf01.entrypoints=sf01"
            - "traefik.udp.routers.rsf01.service=ssf01"
            - "traefik.udp.routers.rsf02.entrypoints=sf02"
            - "traefik.udp.routers.rsf02.service=ssf02"
            - "traefik.udp.routers.rsf03.entrypoints=sf03"
            - "traefik.udp.routers.rsf03.service=ssf03"
            - "traefik.udp.services.ssf01.loadbalancer.server.port=20777"
            - "traefik.udp.services.ssf02.loadbalancer.server.port=20000"
            - "traefik.udp.services.ssf03.loadbalancer.server.port=9898"

        environment:
            - SERVERQUERYPORT=20777
            - SERVERBEACONPORT=20000
            - SERVERGAMEPORT=9898
            - MAXPLAYERS=4
            - PGID=1000
            - PUID=1000
            - STEAMBETA=false
            - AUTOPAUSE=true
            - AUTOSAVENUM=3
            - SKIPUPDATE=false
        restart: always
        deploy:
          resources:
            limits:
              memory: 16G
            reservations:
              memory: 12G

I don't think you can configure Traefik with environment variables. Usually the static configuration is done in command (or using a file via command: --configFile=/path/traefik.yml, but you can not mix) and the dynamic config in labels (or via additional providers like file).

Example docker-compose.yml:

version: '3.9'

services:
  traefik:
    image: traefik:v2.9
    ports:
      - published: 80
        target: 80
        protocol: tcp
        mode: host
      - published: 443
        target: 443
        protocol: tcp
        mode: host
    networks:
      - proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /root/traefik-certificates:/traefik-certificates
    command:
      --providers.docker=true
      --providers.docker.network=proxy
      --providers.docker.exposedByDefault=false
      --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=true
      --api.debug=true
      --api.dashboard=true
      --log.level=DEBUG
      --accesslog=true
      --certificatesResolvers.myresolver.acme.email=mail@example.com
      --certificatesResolvers.myresolver.acme.tlschallenge=true
      --certificatesResolvers.myresolver.acme.storage=/traefik-certificates/acme.json
    labels:
      - traefik.enable=true
      - traefik.http.routers.mydashboard.entrypoints=websecure
      - traefik.http.routers.mydashboard.rule=Host(`traefik.example.com`)
      - traefik.http.routers.mydashboard.tls.certresolver=myresolver
      - traefik.http.routers.mydashboard.service=api@internal
      - traefik.http.routers.mydashboard.middlewares=myauth
      - traefik.http.middlewares.myauth.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/

  whoami:
    image: traefik/whoami:v1.8
    networks:
      - proxy
    labels:
      - traefik.enable=true
      - traefik.http.routers.mywhoami.entrypoints=websecure
      - traefik.http.routers.mywhoami.rule=Host(`example.com`) || Host(`www.example.com`)
      - traefik.http.routers.mywhoami.tls.certresolver=myresolver
      - traefik.http.services.mywhoami.loadbalancer.server.port=80

networks:
  proxy:
    name: proxy
    driver: overlay
    attachable: true

Note: I think network creation in docker-compose.yml only works with Docker Swarm

I found this ressource about env variables: Traefik Environment Variables Documentation - Traefik so I tried to use it.

Thank you for your fast reply, I will try it again with your method.

Did you ever get this sorted?

I'm looking to host a few servers behind treafik and normally all my apps use a single port and it's relatively simple...but game servers tend to need multiple.

What’s your issue? You can proxy multiple ports with Traefik, even UDP. But UDP does not support rules, so a UDP port can only forwarded to a single target service, do routing by domain/path is not possible.

My learnings over the last year:

  • you can use ENV vars to configure Traefik static config (doc), it’s just usually done with traefik.yml or compose command
  • you can create networks in compose, see simple Traefik example

Perhaps I didn't fully understand some of the limitations of treafik in regards to hosting these servers behind it.

I've opened the ports on the fw which seems to work just fine in the interim, but I'd definitely like to point with domains. perhaps I need to do this at the dns level instead.

For reference, here are my compose files.

main docker compose with treafik & oauth

version: "3.7"

# Current: Traefik v2.2.1
# TO DO: update to latest traefik
########################### NETWORKS
networks:
  t2_proxy:
    name: t2_proxy
    external: true
  default:
    driver: bridge

# Common environment values
x-environment: &default-tz-puid-pgid
  TZ: $TZ
  PUID: $PUID
  PGID: $PGID

# Limit log file sizes
x-logging: &default-logging
  logging:
    driver: json-file
    options:
      max-file: "5"
      max-size: 10m

# Proxy Network and Security
x-network-and-security: &network-and-security
  <<: *default-logging
  networks:
    - t2_proxy
  security_opt:
    - no-new-privileges:true

# Keys common to some of the services in basic-services.txt
x-common-keys-core: &common-keys-core
  <<: *network-and-security
  restart: always
  # profiles:
  # - core
  
# Keys common to some of the services in basic-services.txt
x-common-keys-monitoring: &common-keys-monitoring
  <<: *network-and-security
  restart: always
  # profiles:
  # - monitoring

# Keys common to some of the dependent services/apps
x-common-keys-apps: &common-keys-apps
  <<: *network-and-security
  restart: unless-stopped
  # profiles:
  # - apps

# Keys common to some of the services in media-services.txt
x-common-keys-media: &common-keys-media
  <<: *network-and-security
  restart: "no"
  # profiles:
  # - media

########################### SERVICES
services:
# All services / apps go below this line
#
# Traefik 2 - Reverse Proxy
  traefik:
    <<: *common-keys-core # See EXTENSION FIELDS at the top
    container_name: traefik
    image: traefik:2.2.1 # the chevrotin tag refers to v2.2.x but introduced a breaking change in 2.2.2
    restart: unless-stopped
    command: # CLI arguments
      - --global.checkNewVersion=true
      - --global.sendAnonymousUsage=true
      - --entryPoints.http.address=:80
      - --entryPoints.https.address=:443
        # Allow these IPs to set the X-Forwarded-* headers - Cloudflare IPs: https://www.cloudflare.com/ips/
      - --entrypoints.https.forwardedHeaders.trustedIPs=173.245.48.0/20,103.21.244.0/22,103.22.200.0/22,103.31.4.0/22,141.101.64.0/18,108.162.192.0/18,190.93.240.0/20,188.114.96.0/20,197.234.240.0/22,198.41.128.0/17,162.158.0.0/15,104.16.0.0/12,172.64.0.0/13,131.0.72.0/22
      - --entryPoints.traefik.address=:8080
      - --api=true
#      - --api.insecure=true
#      - --serversTransport.insecureSkipVerify=true
      - --log=true
      - --log.level=DEBUG # (Default: error) DEBUG, INFO, WARN, ERROR, FATAL, PANIC
      - --accessLog=true
      - --accessLog.filePath=/traefik.log
      - --accessLog.bufferingSize=100 # Configuring a buffer of 100 lines
      - --accessLog.filters.statusCodes=400-499
      - --providers.docker=true
      - --providers.docker.endpoint=unix:///var/run/docker.sock
      - --providers.docker.defaultrule=Host(`{{ index .Labels "com.docker.compose.service" }}.$DOMAINNAME0`)
      - --providers.docker.exposedByDefault=false
      - --providers.docker.network=t2_proxy
      - --providers.docker.swarmMode=false
      - --providers.file.directory=/rules # Load dynamic configuration from one or more .toml or .yml files in a directory.
#      - --providers.file.filename=/path/to/file # Load dynamic configuration from a file.
      - --providers.file.watch=true # Only works on top level files in the rules folder
      #- --certificatesResolvers.dns-cloudflare.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory # LetsEncrypt Staging Server - uncomment when testing
      - --certificatesResolvers.dns-cloudflare.acme.email=$CLOUDFLARE_EMAIL
      - --certificatesResolvers.dns-cloudflare.acme.storage=/acme.json
      - --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.provider=cloudflare
      - --certificatesResolvers.dns-cloudflare.acme.dnsChallenge.resolvers=1.1.1.1:53,1.0.0.1:53
    ports:
      - target: 80
        published: 80
        protocol: tcp
        mode: host
      - target: 443
        published: 443
        protocol: tcp
        mode: host
      - target: 8080
        published: 8080
        protocol: tcp
        mode: host
    volumes:
      - $DOCKERDIR/traefik2/rules:/rules 
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - $DOCKERDIR/traefik2/acme/acme.json:/acme.json 
      - $DOCKERDIR/traefik2/traefik.log:/traefik.log 
      - $DOCKERDIR/shared:/shared
    environment:
      - CF_API_EMAIL=$CLOUDFLARE_EMAIL
      - CF_API_KEY=$CLOUDFLARE_API_KEY
    labels:
      - "traefik.enable=true"
      # HTTP-to-HTTPS Redirect
      - "traefik.http.routers.http-catchall.entrypoints=http"
      - "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
      - "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      # HTTP Routers
      - "traefik.http.routers.traefik-rtr.entrypoints=https"
      - "traefik.http.routers.traefik-rtr.rule=Host(`traefik.$DOMAINNAME0`)"
      - "traefik.http.routers.traefik-rtr.tls=true"
      #- "traefik.http.routers.traefik-rtr.tls.certresolver=dns-cloudflare" # Comment out this line after first run of traefik to force the use of wildcard certs
      - "traefik.http.routers.traefik-rtr.tls.domains[0].main=$DOMAINNAME0"
      - "traefik.http.routers.traefik-rtr.tls.domains[0].sans=*.$DOMAINNAME0"
#      - "traefik.http.routers.traefik-rtr.tls.domains[1].main=$SECONDDOMAINNAME" # Pulls main cert for second domain
#      - "traefik.http.routers.traefik-rtr.tls.domains[1].sans=*.$SECONDDOMAINNAME" # Pulls wildcard cert for second domain
      ## Services - API
      - "traefik.http.routers.traefik-rtr.service=api@internal"
      ## Middlewares
      - "traefik.http.routers.traefik-rtr.middlewares=chain-oauth@file"
      
# Google OAuth - Single Sign On using OAuth 2.0
  oauth:
    <<: *common-keys-core # See EXTENSION FIELDS at the top
    container_name: oauth
    image: thomseddon/traefik-forward-auth:latest
    restart: unless-stopped
    environment:
      - CLIENT_ID=$GOOGLE_CLIENT_ID
      - CLIENT_SECRET=$GOOGLE_CLIENT_SECRET
      - SECRET=$OAUTH_SECRET
      - COOKIE_DOMAIN=$DOMAINNAME0
      - INSECURE_COOKIE=false
      - AUTH_HOST=oauth.$DOMAINNAME0
      - URL_PATH=/_oauth
      - WHITELIST=$MY_EMAIL0,$MY_EMAIL1
      - LOG_LEVEL=info
      - LOG_FORMAT=text
      - LIFETIME=2592000 # 30 days
      - DEFAULT_ACTION=auth
      - DEFAULT_PROVIDER=google
    labels:
      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.oauth-rtr.entrypoints=https"
      - "traefik.http.routers.oauth-rtr.rule=Host(`oauth.$DOMAINNAME0`)"
      - "traefik.http.routers.oauth-rtr.tls=true"
      ## HTTP Services
      - "traefik.http.routers.oauth-rtr.service=oauth-svc"
      - "traefik.http.services.oauth-svc.loadbalancer.server.port=4181"
      ## Middlewares
      - "traefik.http.routers.oauth-rtr.middlewares=chain-oauth@file"

counterstrike server & rcon panel compose

  rcon:
    <<: *common-keys-games
    image: forewing/webrcon-server
    ports:
      - $WEBRCON_PORT:3001
    restart: unless-stopped

    ## `cs2server` is the game server's service name
    ## if you prefer using config file, comment out previous `command:` line,
    ## create config.json and uncomment next few lines

    command: "-conf /app/config.json"
    volumes:
      - $DOCKERDIR/steam/webrcon/config.json:/app/config.json
    #command: "-addr cs2server:27015"
    labels:
      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.webrcon-rtr.entrypoints=https"
      - "traefik.http.routers.webrcon-rtr.rule=Host(`webrcon.$DOMAINNAME0`)"
      - "traefik.http.routers.webrcon-rtr.tls=true"
#      - "traefik.http.routers.webrcon-rtr.tls.certresolver=dns-cloudflare" 
      ## Middlewares
      - "traefik.http.routers.webrcon-rtr.middlewares=chain-oauth@file"
      ## HTTP Services
      - "traefik.http.routers.webrcon-rtr.service=webrcon-svc"
      - "traefik.http.services.webrcon-svc.loadbalancer.server.port=$WEBRCON_PORT"

  linuxgsm-cs2:
    <<: *common-keys-games
    build:
      context: .
      args: [ "UID=1001", "GID=1001"]
      dockerfile: /dockerconfigs/linuxgsm/dockerfiles/Dockerfile.cs2
    container_name: cs2server
    volumes:
      - $DOCKERDIR/linuxgsm/cs2server/data:/data  # main data dir where gamefiles/configs go
      - $DOCKERDIR/linuxgsm/cs2server/addons:/data/serverfiles/game/csgo/addons  # metamod addon
    restart: unless-stopped
    environment:
      - UID=1001 # found in dockerfile for ubuntu, may not be referenced properly though
      - GID=1001
      - TZ=America\Chicago
    ports:
      - "$CS2_PORT:27015/tcp"
      - "$CS2_PORT:27015/udp"
      - "$CS2_TV_PORT:27020/udp"
      - "$CS2_RCON_PORT:27025/udp"

    #image: gameservermanagers/gameserver:cs2
    ## image: ghcr.io/gameservermanagers/gameserver:csgo
    labels:
      - "traefik.enable=true"
      ## HTTP Routers
      - "traefik.http.routers.gsm-rtr.entrypoints=https"
      - "traefik.http.routers.gsm-rtr.rule=Host(`gsm.$DOMAINNAME0`,`cs2.$DOMAINNAME0`)"
      - "traefik.http.routers.gsm-rtr.tls=true"
#      - "traefik.http.routers.gsm-rtr.tls.certresolver=dns-cloudflare" 
      ## Middlewares
      - "traefik.http.routers.gsm-rtr.middlewares=chain-no-auth@file"
      ## HTTP Services
      - "traefik.http.routers.gsm-rtr.service=gsm-svc"
      - "traefik.http.services.gsm-svc.loadbalancer.server.port=$CS2_PORT"

You need to have all sub-domains in DNS, pointing to Traefik IP.

Have ports open in firewall, open in Docker and declared as Traefik entrypoints.