Files
Cs2-Mapveto/docs/portainer-cs2.md
SPP UserandClaude Haiku 5.5 726974fe82 AMP und feste Host-Namen aus Doku und Tests entfernen
Der Docker-Host ist beliebig; Beispiele nutzen jetzt DOCKER-HOST-IP bzw.
den SSH-Alias docker-host statt eines Rechnernamens. Testfixture heißt
echte-logs-wingman.log.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-25 22:54:38 +01:00

14 KiB
Raw Permalink Blame History

CS2-Server mit Portainer einrichten

Diese Anleitung richtet einen CS2-Dedicated-Server als Portainer-Stack ein, dessen Spieldateien (ca. 60 GB) einmal heruntergeladen und danach dauerhaft in einem Docker-Volume gespeichert werden. Neustarts, Image-Updates und neue Matches laden die Dateien nicht neu.

Anschließend wird der Server mit CS2 Mapveto verbunden.


So funktioniert das Speichern der Dateien

Das Image joedwards32/cs2 installiert das Spiel mit SteamCMD nach /home/steam/cs2-dedicated/. Bei jedem Containerstart prüft es mit app_update, ob es ein Update gibt.

  • Beim ersten Start ist das Verzeichnis leer, also werden die kompletten ~60 GB geladen.
  • Bei jedem weiteren Start liegen die Dateien schon im Volume. SteamCMD vergleicht nur die Version, das dauert Sekunden bis wenige Minuten.
  • Wenn Valve ein Update veröffentlicht, lädt SteamCMD nur die geänderten Teile (Delta-Update).

Entscheidend ist also: Das Verzeichnis /home/steam/cs2-dedicated/ muss auf einem benannten Volume (oder einem Host-Ordner) liegen, nicht im Container selbst. Dann übersteht es auch das Löschen und Neuerstellen des Containers.

Hinweis zum Image: cm2network/cs2 ist die ältere Fassung desselben Projekts und wird seit September 2025 nicht mehr aktualisiert. Verwende joedwards32/cs2 (auch als ghcr.io/joedwards32/cs2). Die Umgebungsvariablen sind identisch.


Voraussetzungen

  • Docker-Host mit Portainer (CE reicht), Linux empfohlen
  • ca. 70 GB freier Speicher für Spieldateien und Updates, idealerweise auf einer SSD
  • Mindestens 2 CPU-Kerne und 4 GB RAM pro gleichzeitig laufendem Server
  • Game Server Login Token (GSLT) für App-ID 730: https://steamcommunity.com/dev/managegameservers. Ohne Token ist der Server nur im LAN-Modus nutzbar (siehe unten). Pro gleichzeitig laufendem Server brauchst du ein eigenes Token.
  • Freigegebene Ports (Router/Firewall), pro Server:
    • Spielport, Standard 27015, UDP und TCP. Über TCP läuft auch RCON, das nur das Backend erreichen muss.
    • CSTV-Port, Standard 27020, UDP

Schritt 1: Stack in Portainer anlegen

  1. Portainer öffnen, die richtige Environment wählen (dein Docker-Host).
  2. Links auf Stacks und dann + Add stack klicken.
  3. Name: cs2
  4. Build method: Web editor. Diesen Inhalt einfügen:
services:
  cs2-1:
    image: joedwards32/cs2:latest
    container_name: cs2-1
    restart: unless-stopped
    environment:
      - SRCDS_TOKEN=${SRCDS_TOKEN}          # GSLT aus Schritt "Voraussetzungen"
      - STEAMAPPVALIDATE=0                  # 0 = nur Update prüfen, NICHT jedes Mal alles verifizieren
      - CS2_SERVERNAME=MaxLAN CS2 #1
      - CS2_PORT=27015
      - CS2_RCONPW=${CS2_RCONPW}            # RCON-Passwort, wird in CS2 Mapveto eingetragen
      - CS2_PW=${CS2_PW}                    # Serverpasswort (CS2 Mapveto setzt pro Match ein eigenes)
      - CS2_MAXPLAYERS=12
      - CS2_SERVER_HIBERNATE=0              # kein Ruhemodus, sonst reagiert RCON verzögert
      - CS2_LAN=0                           # 1 = reiner LAN-Betrieb (siehe unten)
      - CS2_GAMETYPE=0
      - CS2_GAMEMODE=1
      - CS2_MAPGROUP=mg_active
      - CS2_STARTMAP=de_dust2
      - TV_ENABLE=1                         # CSTV für Zuschauer und Stream
      - TV_PORT=27020
      - TV_PW=${TV_PW}
      - TV_RELAY_PW=${TV_PW}
      - TV_DELAY=90
      - TV_AUTORECORD=0
    volumes:
      - cs2-gamefiles:/home/steam/cs2-dedicated/
    ports:
      - "27015:27015/tcp"
      - "27015:27015/udp"
      - "27020:27020/udp"
    stdin_open: true
    tty: true

volumes:
  cs2-gamefiles:
    # Fester Name, damit Portainer keinen Stack-Präfix voranstellt
    # und CS2 Mapveto (Docker-Adapter) dasselbe Volume findet.
    name: cs2-mapveto-gamefiles
  1. Darunter bei Environment variables auf + Add an environment variable klicken und anlegen:

    Name Beispielwert Bedeutung
    SRCDS_TOKEN A1B2C3D4E5F6… dein GSLT; leer lassen für LAN-Modus
    CS2_RCONPW Kx7fQ2mPz9Lw4tNb RCON-Passwort, lang und zufällig
    CS2_PW Lan2026 Serverpasswort
    TV_PW Cstv2026 Passwort für CSTV

    Nur Buchstaben und Ziffern verwenden. Das Startskript des Images setzt die Werte ungeschützt in einen Shell-Befehl ein. Leerzeichen, Klammern, Kommas, Anführungszeichen oder $ führen dazu, dass der Server nicht startet (syntax error near unexpected token) und Docker ihn endlos neu startet.

    So stehen die Zugangsdaten nicht im Stack-Text selbst.

  2. Deploy the stack klicken.


Schritt 2: Ersten Download abwarten

  1. In Portainer auf Containers und dann cs2-1 klicken, danach auf Logs.
  2. Ein Haken bei Auto-refresh logs hilft. Du siehst den SteamCMD-Fortschritt, z. B. Update state (0x61) downloading, progress: 12.34 %.
  3. Je nach Leitung dauert das 30 Minuten bis mehrere Stunden. Den Container in dieser Zeit nicht neu starten.
  4. Fertig ist der Server, wenn im Log Success! App '730' fully installed. steht und danach der Serverstart folgt. Am Ende stehen Zeilen wie GC Connection established for server bzw. Connection to Steam servers successful.

Ab jetzt liegen die Dateien im Volume cs2-mapveto-gamefiles. Unter Volumes siehst du es mit etwa 60 GB.

Test: In der CS2-Konsole connect <Host-IP>:27015; password <CS2_PW> eingeben.


Schritt 3: Updates und Neustarts ohne neuen Download

Aktion in Portainer Was passiert mit den Dateien
Container Restart bleiben erhalten, nur Update-Prüfung
Stack Update the stack (z. B. geänderte Variablen) bleiben erhalten
Re-pull image and redeploy neues Image, Dateien bleiben im Volume
Container Remove und neu deployen bleiben erhalten (Volume ist unabhängig)
Stack löschen Volume bleibt standardmäßig bestehen
Volume löschen (Volumes, Remove) Dateien sind weg, nächster Start lädt 60 GB neu

Wenn Valve CS2 aktualisiert, lehnen Server mit alter Version Spieler ab. Dann reicht ein Restart des Containers, er zieht nur das Delta.

Setze STEAMAPPVALIDATE nur auf 1, wenn Dateien beschädigt sind. Dann werden alle 60 GB geprüft, was sehr lange dauert. Danach wieder auf 0 stellen.


Schritt 4: Mit CS2 Mapveto verbinden

Für einen dauerhaft laufenden Server wie diesen ist der Static-Adapter gedacht. CS2 Mapveto reserviert ihn pro Match, setzt Passwörter und Map per RCON und gibt ihn nach dem Match wieder frei. Der Container läuft dabei durchgehend.

In der .env von CS2 Mapveto:

SERVER_ADAPTER=static
# host:spielport:rconPasswort:cstvPort
CS2_STATIC_SERVERS=192.168.178.50:27015:DEIN_CS2_RCONPW:27020
  • 192.168.178.50 durch die IP des Docker-Hosts ersetzen, die Spieler erreichen. Sie wird ihnen als Verbindungsadresse angezeigt.
  • Das Backend von CS2 Mapveto muss dieselbe Adresse per TCP auf dem Spielport erreichen (RCON).
  • Danach CS2 Mapveto neu starten (npm run dev bzw. npm start).

Prüfen: Ein neues Match erstellen. Im Serverpanel muss nach wenigen Sekunden „Server bereit“ erscheinen, ohne „Demo“. Bleibt es bei „Server startet“, zeigt die Meldung darunter den RCON-Fehler, z. B. falsches Passwort oder Port nicht erreichbar.


Mehrere Matches gleichzeitig

Jeder gleichzeitig laufende Server braucht eigene Ports, ein eigenes GSLT und, wichtig, ein eigenes Installationsverzeichnis. Zwei Container dürfen nicht gleichzeitig dasselbe Volume aktualisieren, sonst können sich SteamCMD-Downloads gegenseitig beschädigen.

Zweiten Server im selben Stack ergänzen:

  cs2-2:
    image: joedwards32/cs2:latest
    container_name: cs2-2
    restart: unless-stopped
    environment:
      - SRCDS_TOKEN=${SRCDS_TOKEN_2}
      - STEAMAPPVALIDATE=0
      - CS2_SERVERNAME=MaxLAN CS2 #2
      - CS2_PORT=27025
      - CS2_RCONPW=${CS2_RCONPW}
      - CS2_PW=${CS2_PW}
      - CS2_SERVER_HIBERNATE=0
      - TV_ENABLE=1
      - TV_PORT=27030
      - TV_PW=${TV_PW}
      - TV_DELAY=90
    volumes:
      - cs2-gamefiles-2:/home/steam/cs2-dedicated/
    ports:
      - "27025:27025/tcp"
      - "27025:27025/udp"
      - "27030:27030/udp"
    stdin_open: true
    tty: true

…und unter volumes: ergänzen:

  cs2-gamefiles-2:
    name: cs2-mapveto-gamefiles-2

In CS2 Mapveto beide eintragen:

CS2_STATIC_SERVERS=192.168.178.50:27015:PASSWORT:27020,192.168.178.50:27025:PASSWORT:27030

Damit der zweite Server nicht erneut 60 GB aus dem Internet lädt, gibt es zwei Wege:

  1. Volume kopieren (einmalig): Server 1 in Portainer stoppen, dann auf dem Docker-Host:
    docker volume create cs2-mapveto-gamefiles-2
    docker run --rm -v cs2-mapveto-gamefiles:/from -v cs2-mapveto-gamefiles-2:/to alpine sh -c "cp -a /from/. /to/"
    
    Danach Server 1 wieder starten und Server 2 deployen. Er findet die Dateien schon vor und prüft nur noch die Version.
  2. Lancache im LAN betreiben, typisch für LAN-Partys: Steam-Downloads werden lokal zwischengespeichert. Jeder weitere Server lädt dann mit LAN-Geschwindigkeit. Im Stack dafür beim Service dns: - <Lancache-IP> ergänzen (siehe Beispiel des Images).

Alternative: CS2 Mapveto startet Container selbst (Docker-Adapter)

Statt dauerhaft laufender Server kann CS2 Mapveto pro Match einen eigenen Container starten und danach wieder entfernen (SERVER_ADAPTER=docker). Damit auch dabei nichts neu geladen wird, nutzt der Adapter das Volume cs2-mapveto-gamefiles. Das ist genau das Volume, das der Stack oben angelegt und befüllt hat.

1. Docker-Zugriff von CS2 Mapveto auf den Docker-Host

Läuft CS2 Mapveto auf einem anderen Rechner als Docker (z. B. Windows-PC → Docker-Host im LAN), steuert es Docker per SSH:

  1. Auf dem Docker-Host den Benutzer in die Docker-Gruppe aufnehmen (einmalig, danach neu anmelden):
    sudo usermod -aG docker server
    
  2. Auf dem Rechner mit CS2 Mapveto einen SSH-Eintrag anlegen (Windows: C:\Users\<Name>\.ssh\config) und die Anmeldung per Schlüssel einrichten. DOCKER-HOST-IP und USER durch deine Werte ersetzen:
    Host docker-host
        HostName DOCKER-HOST-IP
        User USER
    
  3. Testen, auf dem Rechner mit CS2 Mapveto, nicht auf dem Docker-Host:
    docker -H ssh://docker-host ps
    

2. .env von CS2 Mapveto

SERVER_ADAPTER=docker
DOCKER_HOST=ssh://docker-host
# leer = LAN-Modus (sv_lan 1), sonst ein Game Server Login Token
CS2_GSLT=
# Adresse, unter der Spieler den Docker-Host erreichen
CS2_PUBLIC_HOST=192.168.178.111
# Adresse, unter der CS2 Mapveto RCON erreicht – bei getrennten Rechnern ebenfalls der Docker-Host
CS2_RCON_HOST=192.168.178.111
CS2_DOCKER_VOLUME=cs2-mapveto-gamefiles
CS2_DOCKER_IMAGE=joedwards32/cs2:latest
# zwei Ports pro Match (Spiel TCP+UDP, CSTV UDP), in der Firewall freigeben
CS2_PORT_RANGE_START=27100
CS2_PORT_RANGE_END=27139
# Container müssen CS2 Mapveto für die Live-Ergebnisse erreichen
LOG_RECEIVER_URL=http://192.168.178.31:3000

Wichtig dabei:

  • Den Stack-Server stoppen, sobald die Spieldateien geladen sind. Der Stack dient nur zum einmaligen Befüllen und für Updates. Das Volume bleibt beim Stoppen und Löschen des Stacks erhalten.
  • Starts laufen nacheinander: Jeder Match-Container prüft beim Start per SteamCMD auf Updates. Damit nie zwei Container gleichzeitig das Volume aktualisieren, startet CS2 Mapveto den nächsten erst, wenn der vorige seinen Update-Check abgeschlossen hat. Nach einem CS2-Patch dauert der erste Start dadurch länger, die übrigen warten kurz.
  • Ein GSLT gilt nur für einen laufenden Server. Ohne Token (LAN-Modus) gibt es diese Grenze nicht.
  • Hardware: Ein CS2-Server braucht etwa 1–2 CPU-Kerne und 2–3 GB RAM. 6 Kerne und 16 GB reichen für etwa 2–3 gleichzeitige Matches.
  • Plätze: CSTV belegt selbst einen Spielerplatz. Der Adapter startet Container deshalb mit 6 (Wingman) bzw. 12 (5 gegen 5) Plätzen: alle Spieler, CSTV und eine Reserve. Bei festen Servern (Stack) maxplayers genauso hoch setzen, sonst kommt der letzte Spieler nicht drauf.
  • Serverliste: Match-Container tauchen im LAN-Tab von CS2 nicht auf. Der Client sucht dort nur auf den Ports 27015–27020, und Broadcasts erreichen Docker-Container kaum. Spieler verbinden sich über den Befehl bzw. den Knopf „In CS2 verbinden“ auf der Match-Seite. Jedes Match hat ohnehin ein eigenes Passwort.
  • Bots zum Testen: Ohne menschliche Spieler entfernt CS2 Bots zum Matchstart wieder (bot_join_after_player 1). Für Tests CS2_EXTRA_COMMANDS=bot_join_after_player 0;bot_quota 4;bot_quota_mode fill setzen.

Für eine LAN-Party mit festen Servern ist der Static-Adapter mit Stack aus Schritt 1 die einfachere und robustere Wahl.


LAN-Betrieb ohne GSLT

Für reine LAN-Veranstaltungen kann CS2_LAN=1 gesetzt werden (sv_lan 1). Beim Docker-Adapter genügt es, CS2_GSLT leer zu lassen. Der Server ist dann nur im lokalen Netz erreichbar und braucht kein Token. Spieler müssen im selben Netz sein. Für Zuschauer über das Internet oder externe Teams ist ein GSLT nötig.


Fehlersuche

Symptom Ursache / Lösung
Download startet bei jedem Neustart von vorn Volume fehlt oder ist falsch eingehängt. Prüfen, ob /home/steam/cs2-dedicated/ auf ein Volume zeigt (Container, Volumes).
Permission denied beim Download Bei Host-Ordnern statt Volume: chown -R 1000:1000 /pfad/zum/ordner, weil der Container als Benutzer steam (UID 1000) läuft.
Error! App '730' state is 0x202 Zu wenig Speicher. Mindestens 70 GB frei halten.
Spieler werden mit „Server läuft auf älterer Version“ abgewiesen Valve-Update: Container neu starten.
CS2 Mapveto meldet „RCON-Passwort wurde abgelehnt“ CS2_RCONPW im Stack und Passwort in CS2_STATIC_SERVERS vergleichen.
CS2 Mapveto meldet „Zeitüberschreitung“ TCP-Port 27015 vom Backend aus nicht erreichbar (Firewall) oder Server lädt noch.
Server nicht in der Serverliste, aber per IP erreichbar GSLT fehlt oder ist ungültig. Token prüfen, pro Server ein eigenes.