7) RuskDesk Clients bauen ( build_clients.sh )
Die RustDesk-Clients werden auf den Endgeräten installiert, die du fernsteuern oder zur Fernsteuerung freigeben möchtest (z. B. Laptop, PC, Server, ggf. auch Smartphones). Der Client stellt die Verbindung zu deinem selbst betriebenen RustDesk-Server her, zeigt eine Geräte-ID an und ermöglicht anschließend den Remote-Zugriff – entweder innerhalb deines Netzes oder über das Internet.
Damit Nutzer nicht jedes Mal manuell Server-Adresse und Key eintragen müssen, erzeugt das Build-Skript (build_clients.sh) vorkonfigurierte Download-Pakete:
- es lädt automatisch die aktuellste RustDesk-Client-Version (Windows/macOS) herunter,
- erstellt daraus fertige ZIP-Pakete für Windows (verschiedene Start-Optionen) und macOS,
- legt passende Konfigurationsdateien (TOML) ab (min/full),
- aktualisiert die Download-Webseite (index.html) sowie eine VERSION.txt, damit jederzeit sichtbar ist, welche Client-Version gerade bereitgestellt wird.
- Konfigurationen erzeugen
- clients/config/RustDesk2.min.toml
- clients/config/RustDesk2.full.toml
- clients/config/RustDesk2.toml (Alias auf full)
- Download-Webseite aktualisieren
- clients/index.html wird neu geschrieben (Servername, Links, Hinweise)
- clients/VERSION.txt wird neu geschrieben (RustDesk Release-Tag + Build-Zeit)
- Zips erstellen
- Windows: quick/unblock/standard
- macOS: ZIP mit DMG + install.command
- In jedem ZIP liegen zusätzlich README_DE.txt und README_EN.txt
Wichtige Anpassungsstellen im Skript
- Inhalt der Downloadseite: Block cat > “$INDEX_FILE” …
- Inhalte der Readmes: dort wo README_DE.txt / README_EN.txt erzeugt werden
- Automatische Version: kommt aus GitHub “latest release” → landet in VERSION.txt und in den Readmes
Build script erstellen :
nano /opt/containers/rustdesk/build_clients.sh
Inhalt:
#!/usr/bin/env bash
set -euo pipefail
BASE_DIR="/opt/containers/rustdesk"
ENV_FILE="${BASE_DIR}/.env"
need() { command -v "$1" >/dev/null 2>&1 || { echo "Missing dependency: $1" >&2; exit 1; }; }
need curl
need zip
need jq
# Load .env
if [[ -f "$ENV_FILE" ]]; then
set -a
# shellcheck disable=SC1090
source "$ENV_FILE"
set +a
fi
: "${RUSTDESK_HOST:?Missing in .env}"
: "${KEY_PUB:?Missing in .env}"
: "${RUSTDESK_RELAY:?Missing in .env}"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
# Paths
CONF_DIR="${BASE_DIR}/client-config"
CONF_MIN="${CONF_DIR}/RustDesk2.min.toml"
CONF_FULL="${CONF_DIR}/RustDesk2.full.toml"
CONF_DEFAULT="${CONF_DIR}/RustDesk2.toml" # used inside packages (FULL)
OUT_ROOT="${BASE_DIR}/clients"
OUT_WIN_DIR="${OUT_ROOT}/windows"
OUT_MAC_DIR="${OUT_ROOT}/macos"
OUT_CFG_DIR="${OUT_ROOT}/config"
INDEX_FILE="${OUT_ROOT}/index.html"
VERSION_FILE="${OUT_ROOT}/VERSION.txt"
MAC_DEFAULT_ARCH="${MAC_DEFAULT_ARCH:-aarch64}"
mkdir -p "$CONF_DIR" "$OUT_WIN_DIR" "$OUT_MAC_DIR" "$OUT_CFG_DIR"
# --- Generate configs ---
cat > "$CONF_MIN" <<EOF
rendezvous_server = '${RUSTDESK_HOST}:21116'
[options]
custom-rendezvous-server = '${RUSTDESK_HOST}'
key = '${KEY_PUB}'
EOF
cat > "$CONF_FULL" <<EOF
rendezvous_server = '${RUSTDESK_HOST}:21116'
[options]
custom-rendezvous-server = '${RUSTDESK_HOST}'
relay-server = '${RUSTDESK_RELAY}'
key = '${KEY_PUB}'
EOF
# Default used in packages = FULL (with relay)
cp -f "$CONF_FULL" "$CONF_DEFAULT"
# Publish configs
cp -f "$CONF_MIN" "${OUT_CFG_DIR}/RustDesk2.min.toml"
cp -f "$CONF_FULL" "${OUT_CFG_DIR}/RustDesk2.full.toml"
cp -f "$CONF_DEFAULT" "${OUT_CFG_DIR}/RustDesk2.toml"
# --- Fetch release metadata (for VERSION + index) ---
echo "==> Fetching latest RustDesk release metadata..."
REL_JSON="$(curl -fsSL https://api.github.com/repos/rustdesk/rustdesk/releases/latest)"
RUSTDESK_TAG="$(echo "$REL_JSON" | jq -r '.tag_name // "unknown"')"
RUSTDESK_PUBLISHED="$(echo "$REL_JSON" | jq -r '.published_at // ""')"
BUILD_DATE="$(date -u +"%Y-%m-%d %H:%M UTC")"
cat > "$VERSION_FILE" <<EOF
RustDesk version: ${RUSTDESK_TAG}
Published: ${RUSTDESK_PUBLISHED}
Built: ${BUILD_DATE}
Server: ${RUSTDESK_HOST}
EOF
# --- index.html (with version + server) ---
cat > "$INDEX_FILE" <<EOF
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>RustDesk – Downloads</title>
</head>
<body style="font-family: system-ui; max-width: 820px; margin: 40px auto; line-height: 1.55;">
<h1>RustDesk Downloads</h1>
<p style="opacity:.85">
Server: <b>${RUSTDESK_HOST}</b><br>
RustDesk Version: <b>${RUSTDESK_TAG}</b> (siehe auch <a href="/VERSION.txt">VERSION.txt</a>)<br>
In jedem ZIP liegt eine kurze Anleitung (<code>README_DE.txt</code> / <code>README_EN.txt</code>).
</p>
<h2>Windows</h2>
<ol>
<li><b>Option 1 (am einfachsten):</b> <a href="./windows/rustdesk-win-quick.zip">Quick-Start</a></li>
<li><b>Option 2 (Start mit Vorbereitung):</b> <a href="./windows/rustdesk-win-unblock.zip">Unblock</a></li>
<li><b>Option 3 (klassisch):</b> <a href="./windows/rustdesk-win.zip">Standard</a></li>
</ol>
<h2>macOS</h2>
<p><a href="./macos/rustdesk-mac.zip">macOS Download</a></p>
<h2>Konfiguration</h2>
<ul>
<li><a href="/config/RustDesk2.min.toml">Konfiguration (ohne Relay)</a></li>
<li><a href="/config/RustDesk2.full.toml">Konfiguration (mit Relay)</a></li>
</ul>
</body>
</html>
EOF
asset_url() {
local regex="$1"
echo "$REL_JSON" | jq -r --arg re "$regex" '
.assets[] | select(.name | test($re)) | .browser_download_url
' | head -n1
}
WIN_URL="$(asset_url "rustdesk-.*-x86_64\\.exe$")"
[[ -n "${WIN_URL:-}" && "$WIN_URL" != "null" ]] || { echo "Could not find Windows x86_64 exe" >&2; exit 1; }
MAC_ARM_URL="$(asset_url "rustdesk-.*-aarch64\\.dmg$")"
MAC_INTEL_URL="$(asset_url "rustdesk-.*-x86_64\\.dmg$")"
WIN_EXE="${TMP_DIR}/rustdesk.exe"
curl -fL "$WIN_URL" -o "$WIN_EXE"
# --------------------------
# Windows Option 3: STANDARD
# --------------------------
echo "==> Building Windows STANDARD package (rustdesk-win.zip)..."
WIN_STD="${TMP_DIR}/win_std"
mkdir -p "$WIN_STD"
cp -f "$WIN_EXE" "$WIN_STD/rustdesk.exe"
cp -f "$CONF_DEFAULT" "$WIN_STD/RustDesk2.toml"
cat > "$WIN_STD/start.cmd" <<'EOF'
@echo off
setlocal
set "SCRIPT_DIR=%~dp0"
set "CFGDIR=%APPDATA%\RustDesk\config"
mkdir "%CFGDIR%" >nul 2>&1
copy /Y "%SCRIPT_DIR%RustDesk2.toml" "%CFGDIR%\RustDesk2.toml" >nul
start "" "%SCRIPT_DIR%rustdesk.exe"
endlocal
EOF
cat > "$WIN_STD/README_DE.txt" <<EOF
RustDesk (Windows) – Standard
Server: ${RUSTDESK_HOST}
Version: ${RUSTDESK_TAG}
1) ZIP entpacken.
2) "start.cmd" ausführen.
Das Script kopiert die Konfiguration und startet RustDesk.
EOF
cat > "$WIN_STD/README_EN.txt" <<EOF
RustDesk (Windows) – Standard
Server: ${RUSTDESK_HOST}
Version: ${RUSTDESK_TAG}
1) Unzip the file.
2) Run "start.cmd".
The script installs the configuration and starts RustDesk.
EOF
rm -f "${OUT_WIN_DIR}/rustdesk-win.zip"
( cd "$WIN_STD" && zip -qr "${OUT_WIN_DIR}/rustdesk-win.zip" . )
# -----------------------------------------
# Windows Option 2: UNBLOCK + Fallback
# -----------------------------------------
echo "==> Building Windows UNBLOCK package (rustdesk-win-unblock.zip)..."
WIN_UNBLOCK="${TMP_DIR}/win_unblock"
mkdir -p "$WIN_UNBLOCK"
cp -f "$WIN_EXE" "$WIN_UNBLOCK/rustdesk.exe"
cp -f "$CONF_DEFAULT" "$WIN_UNBLOCK/RustDesk2.toml"
cat > "$WIN_UNBLOCK/start.cmd" <<'EOF'
@echo off
setlocal
set "SCRIPT_DIR=%~dp0"
set "CFGDIR=%APPDATA%\RustDesk\config"
rem Versucht, die Windows-Download-Markierung zu entfernen (falls vorhanden)
for %%F in ("%SCRIPT_DIR%rustdesk.exe" "%SCRIPT_DIR%RustDesk2.toml") do (
>nul 2>&1 del "%%~fF:Zone.Identifier"
)
mkdir "%CFGDIR%" >nul 2>&1
copy /Y "%SCRIPT_DIR%RustDesk2.toml" "%CFGDIR%\RustDesk2.toml" >nul
>nul 2>&1 del "%CFGDIR%\RustDesk2.toml:Zone.Identifier"
start "" "%SCRIPT_DIR%rustdesk.exe"
endlocal
EOF
cat > "$WIN_UNBLOCK/start-original.cmd" <<'EOF'
@echo off
setlocal
set "SCRIPT_DIR=%~dp0"
set "CFGDIR=%APPDATA%\RustDesk\config"
mkdir "%CFGDIR%" >nul 2>&1
copy /Y "%SCRIPT_DIR%RustDesk2.toml" "%CFGDIR%\RustDesk2.toml" >nul
start "" "%SCRIPT_DIR%rustdesk.exe"
endlocal
EOF
cat > "$WIN_UNBLOCK/README_DE.txt" <<EOF
RustDesk (Windows) – Start mit Vorbereitung
Server: ${RUSTDESK_HOST}
Version: ${RUSTDESK_TAG}
1) ZIP entpacken.
2) "start.cmd" ausführen.
Falls es Probleme gibt: "start-original.cmd" verwenden.
EOF
cat > "$WIN_UNBLOCK/README_EN.txt" <<EOF
RustDesk (Windows) – Start with preparation
Server: ${RUSTDESK_HOST}
Version: ${RUSTDESK_TAG}
1) Unzip the file.
2) Run "start.cmd".
If anything goes wrong, use "start-original.cmd".
EOF
rm -f "${OUT_WIN_DIR}/rustdesk-win-unblock.zip"
( cd "$WIN_UNBLOCK" && zip -qr "${OUT_WIN_DIR}/rustdesk-win-unblock.zip" . )
# -----------------------
# Windows Option 1: QUICK
# -----------------------
echo "==> Building Windows QUICK package (rustdesk-win-quick.zip)..."
WIN_QUICK="${TMP_DIR}/win_quick"
mkdir -p "$WIN_QUICK"
KEY_PUB_SAFE="$(printf '%s' "$KEY_PUB" | tr '+/' '-_')"
RENAMED_EXE="rustdesk-host=${RUSTDESK_HOST},key=${KEY_PUB_SAFE}.exe"
cp -f "$WIN_EXE" "$WIN_QUICK/$RENAMED_EXE"
cat > "$WIN_QUICK/README_DE.txt" <<EOF
RustDesk (Windows) – Quick Start
Server: ${RUSTDESK_HOST}
Version: ${RUSTDESK_TAG}
1) ZIP entpacken.
2) Starte die Datei: ${RENAMED_EXE}
Wenn Quick nicht zuverlässig funktioniert:
Bitte die "Unblock" oder "Standard" Variante verwenden.
EOF
cat > "$WIN_QUICK/README_EN.txt" <<EOF
RustDesk (Windows) – Quick Start
Server: ${RUSTDESK_HOST}
Version: ${RUSTDESK_TAG}
1) Unzip the file.
2) Start: ${RENAMED_EXE}
If Quick is not reliable:
Use the "Unblock" or "Standard" package.
EOF
rm -f "${OUT_WIN_DIR}/rustdesk-win-quick.zip"
( cd "$WIN_QUICK" && zip -qr "${OUT_WIN_DIR}/rustdesk-win-quick.zip" . )
# ----------------
# macOS package
# ----------------
echo "==> Building macOS package (rustdesk-mac.zip)..."
MAC_PKG="${TMP_DIR}/mac_pkg"
mkdir -p "$MAC_PKG"
MAC_DMG_URL=""
if [[ "$MAC_DEFAULT_ARCH" == "x86_64" && -n "${MAC_INTEL_URL:-}" && "$MAC_INTEL_URL" != "null" ]]; then
MAC_DMG_URL="$MAC_INTEL_URL"
else
MAC_DMG_URL="$MAC_ARM_URL"
fi
[[ -n "${MAC_DMG_URL:-}" && "$MAC_DMG_URL" != "null" ]] || { echo "Could not find macOS dmg" >&2; exit 1; }
curl -fL "$MAC_DMG_URL" -o "$MAC_PKG/RustDesk.dmg"
cp -f "$CONF_DEFAULT" "$MAC_PKG/RustDesk2.toml"
cat > "$MAC_PKG/install.command" <<'EOF'
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
open "$SCRIPT_DIR/RustDesk.dmg"
echo "DMG ist geöffnet. Bitte RustDesk nach /Applications ziehen (falls noch nicht passiert)."
echo "Dann ENTER drücken, um die Konfiguration zu installieren..."
read -r
CFGDIR="$HOME/Library/Preferences/com.carriez.RustDesk"
mkdir -p "$CFGDIR"
cp -f "$SCRIPT_DIR/RustDesk2.toml" "$CFGDIR/RustDesk2.toml"
echo "Konfiguration installiert."
open -a "RustDesk" || true
EOF
chmod +x "$MAC_PKG/install.command"
cat > "$MAC_PKG/README_DE.txt" <<EOF
RustDesk (macOS)
Server: ${RUSTDESK_HOST}
Version: ${RUSTDESK_TAG}
1) ZIP entpacken.
2) "install.command" ausführen.
3) RustDesk (aus dem DMG) nach /Applications ziehen (falls nötig).
EOF
cat > "$MAC_PKG/README_EN.txt" <<EOF
RustDesk (macOS)
Server: ${RUSTDESK_HOST}
Version: ${RUSTDESK_TAG}
1) Unzip the file.
2) Run "install.command".
3) Drag RustDesk (from the DMG) to /Applications if needed.
EOF
rm -f "${OUT_MAC_DIR}/rustdesk-mac.zip"
( cd "$MAC_PKG" && zip -qr "${OUT_MAC_DIR}/rustdesk-mac.zip" . )
echo "==> Done."
echo "Index: ${INDEX_FILE}"
echo "Version: ${VERSION_FILE}"
echo "Windows quick: ${OUT_WIN_DIR}/rustdesk-win-quick.zip"
echo "Windows unblock: ${OUT_WIN_DIR}/rustdesk-win-unblock.zip"
echo "Windows standard:${OUT_WIN_DIR}/rustdesk-win.zip"
echo "macOS: ${OUT_MAC_DIR}/rustdesk-mac.zip"Code-Sprache: PHP (php)
Build ausführen:
chmod +x /opt/containers/rustdesk/build_clients.sh
cd /opt/containers/rustdesk
./build_clients.sh
8) RustDesk starten
Hinweis: Damit alles funktioniert muss natürlich auf dem Host alles geregelt sein ! Siehe dazu 10.5
cd /opt/containers/rustdesk
docker compose up -d
docker compose ps
Container-Status + Health anzeigen
cd /opt/containers/rustdesk #optional wenn nicht im Order
docker compose ps
docker inspect -f '{{.State.Health.Status}}' rustdesk 2>/dev/null || trueCode-Sprache: PHP (php)
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
rustdesk ghcr.io/rustdesk/rustdesk-server-s6:latest "/init" rustdesk 4 days ago Up 4 days (healthy) 21115-21119/tcp, 21116/udp
rustdesk-clients joseluisq/static-web-server:2-alpine "/usr/local/bin/entr…" rustdesk-clients 4 days ago Up 4 days 80/tcp
healthyCode-Sprache: JavaScript (javascript)

Hallo Frank,
ich habe in meinem trafik-crowsec-stack schon einen Container laufen, welcher über Port 80/443 angebunden ist. Wie schaut den die config aus, wenn ich den Rustdesk-Webserver auf Port 8000 betreiben möchte und über https 4443 zugreifen möchte? Ich habe mir zwar schon die Ports soweit eingebaut, aber es hapert bei mir noch mit den EntryPoints im trafik-crowsec-stack
2.Thema: Kann man auch die Linux-Clients (z.B. Debian) bauen lassen mit dem Skipt?
Hallo Frank, danke für die Anleitung und auch die super Idee mit der Downloadseite.
Mir ist ein kleiner Fehler beim Generieren der Windows Quick-Start-EXE über dein build_client.sh Scriptaufgefallen.
Problem: Im Abschnitt
Windows Option 1: QUICKwird am Ende des Dateinamens hartcodiert ein zusätzliches Gleichheitszeichen eingefügt:RENAMED_EXE="rustdesk-host=${RUSTDESK_HOST},key=${KEY_PUB_SAFE}=.exe"Dies führt dazu, dass der Public Key im Dateinamen ungültig wird (da er nun auf ein
=zu viel endet), wodurch der RustDesk-Client den Key nicht mehr korrekt ausliest und keine Verbindung zum Server herstellen kann.Lösung: Das zusätzliche
=vor der Dateiendung muss entfernt werden, da der Key in der Variable${KEY_PUB_SAFE}bereits das korrekte Base64-Format (inkl. notwendigem Padding) besitzt.# Vorher: RENAMED_EXE=“rustdesk-host=${RUSTDESK_HOST},key=${KEY_PUB_SAFE}=.exe”
# Nachher: RENAMED_EXE=“rustdesk-host=${RUSTDESK_HOST},key=${KEY_PUB_SAFE}.exe”
Hallo Frank, danke nochmal für die tolle Anleitung.
Ich habe Aliase zum Öffnen und Schließen der Ports angelegt, weil ich Rustdesk nur gelegentlich brauche und ich die Ports nicht dauerhaft geöffnet haben möchte.
Wer die ufw aktiviert hat muss die folgenden Befehle nehmen:
# Zum Öffnen der Ports
sudo ufw allow proto tcp from any to any port 21115,21116,21117 && sudo ufw allow 21116/udp && sudo ufw status
# Zum Schließen der Ports
sudo ufw deny proto tcp from any to any port 21115,21116,21117 && sudo ufw deny 21116/udp && sudo ufw status
Zum dauerhaften Anlegen der Aliase kann ich das Ubuntu-Wiki empfehlen, da ist das Anlegen der Aliase sehr gut beschrieben.
Hallo
Frank, ich habe Rustdesk mal neu nach deiner Anleitung installiert und es hat alles geklappt. Ich kann Verbindungen aufbauen. Aber nur wenn ich den Relayserver mit eintrage. Auch die Webseite mit dem Clients ist echt super. Ich muss mich mal noch weiter damit beschäftigen. Aber vielen Dank für deine gute Anleitung.