Skip to content

Instantly share code, notes, and snippets.

@hadim
Last active December 30, 2025 06:11
Show Gist options
  • Select an option

  • Save hadim/2225f0869ada523dec8148510ca438ca to your computer and use it in GitHub Desktop.

Select an option

Save hadim/2225f0869ada523dec8148510ca438ca to your computer and use it in GitHub Desktop.
RustDesk and Traefik Docker Compose Configuration
# A minimal configuration to host a RustDesk server with Traefik v3.
#
# This configuration is based on a single Rustdesk container hosting the two hbbr and hbbs services
# instead of running two separate containers as in the official documentation.
# See https://rustdesk.com/docs/en/self-host/rustdesk-server-oss/docker/ for more information.
#
# Pay attention to the comments in the file and adapt the configuration to your needs.
# Once deployed you must configure the Rustdesk client in the Network tab to use the domain
# name of the relay server and the port 21117.
# ID server: DOMAIN_NAME:21116
# Relay server: DOMAIN_NAME:21117
# Key: THE_PUBLIC_KEY
#
# Note that 21116/udp is causing connections issue with Rustdesk so we
# directly open the port in the rustdesk container instead of going through Traefik.
version: "3"
services:
# Traefik proxy
#
# This traefik configuration is the strict minimum to make Rustdesk work without any other services
# such as Traefik dashboard. Adapt it to your needs.
traefik:
container_name: traefik
image: traefik:v3.2
ports:
# Rustdesk ports
# Those MUST be open on your router and firewall and forwarded to the host running
# this docker-compose file.
- 21115:21115/tcp # ID Server - NAT type test
- 21116:21116/tcp # ID Server - TCP hole punching
- 21117:21117/tcp # Relay Server - Relay services
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped
command:
- --global.checkNewVersion=false
- --global.sendAnonymousUSage=false
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --log.level=INFO
- --accesslog.filters.statuscodes=400-499
# Https entrypoints
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.websecure.address=:443
- --certificatesresolvers.mydnschallenge.acme.email=YOUR_EMAIL
- --certificatesresolvers.mydnschallenge.acme.storage=/data/acme.json
- --certificatesresolvers.mydnschallenge.acme.httpchallenge.entrypoint=web
# Rustdesk entrypoints
- --entrypoints.rd5-tcp.address=:21115/tcp # ID Server - NAT type test
- --entrypoints.rd6-tcp.address=:21116/tcp # ID Server - TCP hole punching
- --entrypoints.rd7-tcp.address=:21117/tcp # Relay Server - Relay services
# RustDesk ID/Rendezvous server and Relay server
#
# This container uses rustdesk-server-s6 instead of rustdesk-server. The difference is that
# rustdesk-server-s6 is based on s6-overlay which runs the two services hbbr and hbbs in the same
# container.
rustdesk:
container_name: rustdesk
image: ghcr.io/rustdesk/rustdesk-server-s6:latest
volumes:
- LOCAL_DIR_TO_HOST_THE_DATABASE:/db
- LOCAL_DIR_TO_HOST_THE_KEYS:/data
restart: unless-stopped
environment:
- RELAY=DOMAIN_NAME:21117
- ENCRYPTED_ONLY=1
- DB_URL=/db/db_v2.sqlite3
# The private and public keys must be generated with the following command:
# Generate the private key: `openssl genpkey -algorithm Ed25519 -out private.key`
# Generate the public key: `openssl pkey -in private.key -pubout -out public.key`
# Display botk keys and copy them below: `cat private.key public.key`
# openssl rsa -in private.pem -pubout -out public.pem
- KEY_PRIV=THE_PRIVATE_KEY
- KEY_PUB=THE_PUBLIC_KEY
- RUST_LOG=debug
ports:
# Rustdesk
# Note that 21116/udp is causing connections issue with Rustdesk so we
# directly open the port in the rustdesk container instead of going through Traefik.
- 21116:21116/udp # ID Server - ID registration and heartbeat
labels:
- traefik.enable=true
# 21115/tcp # ID Server - NAT type test
- traefik.tcp.routers.rustdesk-rd5.entrypoints=rd5-tcp
- traefik.tcp.routers.rustdesk-rd5.rule=HostSNI(`*`)
- traefik.tcp.routers.rustdesk-rd5.service=rustdesk-rd5
- traefik.tcp.services.rustdesk-rd5.loadbalancer.server.port=21115
- traefik.tcp.routers.rustdesk-rd5.tls=false
- traefik.tcp.routers.rustdesk-rd5.tls.passthrough=true
# 21116/tcp # ID Server - TCP hole punching
- traefik.tcp.routers.rustdesk-rd6.entrypoints=rd6-tcp
- traefik.tcp.routers.rustdesk-rd6.rule=HostSNI(`*`)
- traefik.tcp.routers.rustdesk-rd6.service=rustdesk-rd6
- traefik.tcp.services.rustdesk-rd6.loadbalancer.server.port=21116
- traefik.tcp.routers.rustdesk-rd6.tls=false
- traefik.tcp.routers.rustdesk-rd6.tls.passthrough=true
# 21117/tcp # Relay Server - Relay services
- traefik.tcp.routers.rustdesk-rd7.entrypoints=rd7-tcp
- traefik.tcp.routers.rustdesk-rd7.rule=HostSNI(`*`)
- traefik.tcp.routers.rustdesk-rd7.service=rustdesk-rd7
- traefik.tcp.services.rustdesk-rd7.loadbalancer.server.port=21117
- traefik.tcp.routers.rustdesk-rd7.tls=false
- traefik.tcp.routers.rustdesk-rd7.tls.passthrough=true
# 21118/http # ID Server - Web client
- traefik.http.routers.rustdesk-rd8.rule=Host(`DOMAIN_NAME`) && PathPrefix(`/ws/id`)
- traefik.http.routers.rustdesk-rd8.entrypoints=websecure
- traefik.http.routers.rustdesk-rd8.service=rustdesk-rd8
- traefik.http.services.rustdesk-rd8.loadbalancer.server.port=21118
- traefik.http.routers.rustdesk-rd8.tls.certresolver=mydnschallenge
- traefik.http.routers.rustdesk-rd8.middlewares=rustdesk-headers
# 21119/http # Relay Server - Web client
- traefik.http.routers.rustdesk-rd9.rule=Host(`DOMAIN_NAME`) && PathPrefix(`/ws/relay`)
- traefik.http.routers.rustdesk-rd9.entrypoints=websecure
- traefik.http.routers.rustdesk-rd9.service=rustdesk-rd9
- traefik.http.services.rustdesk-rd9.loadbalancer.server.port=21119
- traefik.http.routers.rustdesk-rd9.tls.certresolver=mydnschallenge
- traefik.http.routers.rustdesk-rd9.middlewares=rustdesk-headers
# Headers configurations for rustdesk-rd8 and rustdesk-rd8
- traefik.http.middlewares.rustdesk-headers.headers.customRequestHeaders.X-Forwarded-Proto=https
- traefik.http.middlewares.rustdesk-headers.headers.customRequestHeaders.X-Real-IP=true
- traefik.http.middlewares.rustdesk-headers.headers.customRequestHeaders.Host=Host(`DOMAIN_NAME`)
@prov3it
Copy link

prov3it commented Mar 6, 2024

Hi,

Could you help me out? Im following your instructions to the letter but i get an error (docker logs):

s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service key-secret: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
**Invalid Secret key
Key pair not valid**
s6-rc: warning: unable to start service key-secret: command exited 1
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped

I tried multiple things:

  1. Created the ed25519 keys and put them straight into the data-folder, including:
-----BEGIN PRIVATE KEY-----
KEY_HERE
-----END PRIVATE KEY-----
  1. Add key to docker-compose.yml:
version: "3"
services:
  rustdesk-server:
    image: rustdesk/rustdesk-server-s6:latest
    hostname: rustdesk-server
    container_name: rustdesk-server
    restart: unless-stopped
    environment:
      - RELAY=rustdesk.domain.tld:21117
      - ENCRYPTED_ONLY=1
      - KEY_PRIV="MC4CAQAwBQYDK2VwBCIEIPmqsNFAy48dn9HkRq0TIMUfEAtJGQVR4vBnkzVL0yqp"
      - KEY_PUB="MCowBQYDK2VwAyEAcYSptRi+SbMAimuN3vW7bsf84BwP0ILxEWYuRdKIYEo="
      - DB_URL=/db/db_v2.sqlite3
      - RUST_LOG=debug
    volumes:
      - /some/path/data/rustdesk/db:/db
      - /some/path/data/rustdesk/data:/data
    networks:
      - network
    labels (other labels are in file and not the issue):
      - traefik.enable=true
    
networks:
  network:
    external: true

Also, what do i need to do with: "openssl rsa -in private.pem -pubout -out public.pem" ? Doesnt seem to be connected with the commands above.

@axodentally
Copy link

as per official documentation the keys can be generated using docker run --rm --entrypoint /usr/bin/rustdesk-utils rustdesk/rustdesk-server-s6:latest genkeypair. This worked for me, the gist should be updated with this command.

@macidev
Copy link

macidev commented Jan 18, 2025

Hi, just a quick question, I get multiple error messages regarding the entry points

# Rustdesk entrypoints - --entrypoints.rd5-tcp.address=:21115/tcp # ID Server - NAT type test - --entrypoints.rd6-tcp.address=:21116/tcp # ID Server - TCP hole punching - --entrypoints.rd7-tcp.address=:21117/tcp # Relay Server - Relay services

The traefik logs state that the entry points do not exist. I tried to set them in my traefik.yml and within the command section.

@TheJimson
Copy link

Hi, just a quick question, I get multiple error messages regarding the entry points

# Rustdesk entrypoints - --entrypoints.rd5-tcp.address=:21115/tcp # ID Server - NAT type test - --entrypoints.rd6-tcp.address=:21116/tcp # ID Server - TCP hole punching - --entrypoints.rd7-tcp.address=:21117/tcp # Relay Server - Relay services

The traefik logs state that the entry points do not exist. I tried to set them in my traefik.yml and within the command section.

I wasted a couple of hours with the same issue. Then I remembered I also had a traefik.yml in addition to my docker-compose.yml (where I added the above config).
Creating entrypoints in traefik.yml sorted the issue.

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
  rd5-tcp:
    address: ":21115/tcp"
  rd6-tcp:
    address: ":21116/tcp"
  rd7-tcp:
    address: ":21117/tcp"

@sahara101
Copy link

Hi, I do not understand how the web client works. If i try the URL i get 404 not found. Must I do something extra?

@di5cord20
Copy link

di5cord20 commented Mar 2, 2025

Probably the wrong post, I'm curious if anyone has managed to get rustdesk working with Caddy. I haven't found much by way of Google and ChatGPT couldn't solve (for me).

I'm also running as an LXC (not docker) via https://community-scripts.github.io/ProxmoxVE/scripts?id=rustdeskserver

@pxsh64zz
Copy link

pxsh64zz commented Dec 6, 2025

Greetings. Is your setup working in December 2025? I recently installed traefik on a fresh VPS and have stock rustdesk running but would prefer to have it behind traefik. I will tinker with your setup, thank you.

@hadim
Copy link
Author

hadim commented Dec 6, 2025

Greetings. Is your setup working in December 2025? I recently installed traefik on a fresh VPS and have stock rustdesk running but would prefer to have it behind traefik. I will tinker with your setup, thank you.

Still working so far.

@alpsware
Copy link

alpsware commented Dec 30, 2025

Thanks to @hadim shared docker compose file, we created our own configuration for websockets only just using port 443. Its still possible to use with TCP, just uncomment the ports section and then it should work.

https://gist.github.com/alpsware/dba8debc1ac1099e0054ea03c70418bb

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment