Skip to content

Architektur

ElGregor edited this page Aug 15, 2026 · 1 revision

Architektur

Home

ProjectZomboiD ist ein Config-as-Code-Setup: Der gewünschte Zustand des Servers liegt als deklarative Dateien im Repo, Generatoren und Skripte setzen ihn idempotent um. Diese Seite erklärt Prinzipien, Komponenten, Datenflüsse und Verzeichnisse.

Prinzipien

  1. Config-as-Code: .env + Templates erzeugen die Server-Configs — Server-INIs werden nie von Hand editiert (Repo-Prinzip, README/docs/01-architektur.md).
  2. Single Source of Truth: Mods leben ausschließlich in mods.yaml, Spawn-Punkte in spawns.yaml, Settings in .env. Generierte Dateien sind Abfallprodukte.
  3. Idempotenz: Jedes Skript ist mehrfach ausführbar, ohne Schaden anzurichten (Repo-Prinzip, docs/01; install.sh nennt es explizit).
  4. Fail-safe Updates: Kein Update ohne Pre-Backup + Healthcheck + Auto-Rollback (Repo-Prinzip, README/docs/01).
  5. Alles per Git nachvollziehbar: Jede Mod-/Config-Änderung ist ein Commit — revertbar und historisiert (Repo-Regel, README).
  6. Ein CLI: make ist die einzige Oberfläche, die man sich merken muss (Repo-Prinzip, docs/01).

Komponenten

Wer ruft wen — von oben nach unten:

Ebene Komponente Zweck Gerufen von
CLI Makefile Einheitliches CLI, 16 Targets (make help zeigt alle) Admin
CLI make install/update/backup/... Delegieren an die Skripte Admin, Timer
Skripte scripts/lib/common.sh Logging, .env-Loader, Discord-Notify, Guards alle Bash-Skripte
Skripte scripts/install.sh SteamCMD + App 380870 + Verzeichnisse + Render + Mods (idempotent) make install
Skripte scripts/render-config.sh envsubst-Rendern der Templates nach $PZ_DATA_DIR/Server make render, install.sh
Skripte scripts/mods-generate.sh / mods-validate.sh mods.yaml → WorkshopItems/Mods/Map; Validierung make mods
Skripte scripts/spawns-generate.sh spawns.yaml → Lua make spawns
Skripte scripts/update.sh Announce → Stop → Backup → Update → Render → Start → Healthcheck → Auto-Rollback make update, Update-Timer
Skripte scripts/backup.sh / restore.sh tar.gz-Rotation (7 täglich / 4 wöchentlich, Repo-Default) bzw. interaktiver Restore make backup/make restore, Backup-Timer
Skripte scripts/monitor.sh Cron-Watchdog mit .planned-stop-Flag (respektiert geplante Stops) Cron, minütlich
Skripte scripts/healthcheck.sh Prozess + UDP-Port + Journal-Fehler-Scan make status/make healthcheck, update.sh
Skripte scripts/announce.sh RCON-Countdowns an Spieler vor Stop/Update (via mcrcon) update.sh
Skripte scripts/notify.sh Manueller Discord-Ping für den Admin (notify.sh "Titel" "Nachricht" [farbe]); automatisierte Skripte rufen stattdessen notify() aus common.sh direkt auf Admin (manueller Aufruf)
Init systemd/zomboid.service Startet start-server.sh -servername ${SERVER_NAME} -Xmx${RAM_XMX}; Restart=always, LimitNOFILE=65535 (Repo-Default, Unit-Datei) systemd
Init systemd/zomboid-*.timer Backup täglich 04:00 (Persistent), Update im templated Fenster systemd
Runtime Spielserver-Prozess Project Zomboid Dedicated Server (App 380870) systemd/Docker
Container docker/docker-compose.yml Optionaler Pfad: Service zomboid, Ports 16261/udp + 16262/udp + 27015/tcp, Healthcheck pgrep -f ProjectZomboid Docker — Details Docker-Betrieb
CI .github/workflows/ci.yml shellcheck + mods-validate + YAML-Check + Secret-Scan bei jedem Push Git-Push
Notfall gcp/ Gestoppte Failover-Instanz + Backup-Sync Admin — Details GCP-Failover

Zahlenwerte der Tabelle sind Repo-Stand bzw. Repo-Default: 16 Targets (1:1 aus Makefile), App 380870 (Steam-App-ID des PZ Dedicated Servers, so vom Repo verwendet), Ports 16261/udp + 16262/udp + 27015/tcp (docker-compose.yml), Backup-Timer 04:00 Uhr (zomboid-backup.timer) — jeweils aus den genannten Repo-Dateien übernommen.

Datenfluss

(a) Mods: mods.yaml bis Server-INI

mods.yaml
  └─(make mods → mods-generate.sh, Sortierung nach Load-Order-Kategorien)
      config/generated/workshop-items.txt   (WorkshopItems= / Mods= / Map=)
  └─(manuelle Übernahme der drei Zeilen in die Server-INI — kein Skript macht das)
      ${PZ_DATA_DIR}/Server/servertest.ini   (Mod-Zeilen von Hand einpflegen)
  └─(Server-Restart)

Repo-Stand: Die Übernahme der WorkshopItems=/Mods=/Map=-Zeilen in die Server-INI ist ein manueller Schritt. render-config.sh rendert nur config/templates/*.tmpl per envsubst — dort sind die Mod-Zeilen auskommentiert (# WorkshopItems=... usw., servertest.ini.tmpl) — und mods-generate.sh weist im Log lediglich darauf hin, die Zeilen in die Server-INI zu übernehmen (wörtlich: „in ${SERVER_NAME}.ini uebernehmen"; mit dem Repo-Default SERVER_NAME=servertest meint das die servertest.ini). Eine bekannte Eigenheit des Repo-Stands, kein Automatismus.

Das Prinzip „Server-INIs nie von Hand editieren" gilt daher für die per envsubst erzeugten Werte — ausgenommen sind die Mod-Zeilen, die bewusst manuell aus workshop-items.txt gepflegt werden müssen.

Der Generator sortiert aktive Mods nach den Kategorien library → framework → map → gameplay → qol (= Load-Order), setzt den B42-Pflicht-Backslash vor jede Mod-ID und hängt Muldraugh, KY in der Map=-Zeile immer zuletzt (Repo-Verhalten, mods-generate.sh).

(b) Settings: .env bis Zomboid-Verzeichnis

.env
  └─(render-config.sh / envsubst)
      config/templates/servertest.ini.tmpl
      → ${PZ_DATA_DIR}/Server/servertest.ini                 (Hauptconfig — Dateiname = Template-Basisname)
      → ${PZ_DATA_DIR}/Server/${SERVER_NAME}_SandboxVars.lua (Sandbox-Preset coop-balanced)
      → ${PZ_DATA_DIR}/Server/${SERVER_NAME}_*.lua           (Spawn-Dateien, per sed umbenannt)

envsubst ersetzt dabei genau die 12 aufgezählten Variablen aus der VARS-Liste in render-config.sh (inkl. SERVER_NAME innerhalb der INI-Werte). SERVER_NAME bestimmt aber nicht den INI-Dateinamen: Die Hauptconfig heißt immer servertest.ini (Template-Basisname, Repo-Verhalten render-config.sh); SERVER_NAME benennt nur die Sandbox-Preset-Datei (${SERVER_NAME}_SandboxVars.lua) und steuert die Umbenennung der Spawn-Lua-Dateien (sed s/^servertest/${SERVER_NAME}/).

Bekannte Eigenheit: Der Serverprozess wird mit -servername ${SERVER_NAME} gestartet und erwartet daher ${PZ_DATA_DIR}/Server/${SERVER_NAME}.ini. Mit dem Repo-Default SERVER_NAME=servertest fällt das mit der gerenderten servertest.ini zusammen; weicht SERVER_NAME davon ab, muss die gerenderte Datei manuell auf den passenden Namen gebracht werden — dafür gibt es im Repo-Stand keine Automatik.

(c) Spawns: spawns.yaml bis Lua

spawns.yaml
  └─(make spawns → spawns-generate.sh)
      config/generated/spawns/*.lua
  └─(render-config.sh kopiert; generierte Version hat Vorrang vor config/spawns/)
      ${PZ_DATA_DIR}/Server/${SERVER_NAME}_spawnregions.lua usw.

Repo-Struktur

Verifiziert am Clone (Stand: Commit 11, main):

ProjectZomboiD/
├── Makefile                      Ein CLI: alle Targets (make help)
├── .env.example                  Vorlage für .env: Server-Basis, Pfade, RAM, Ports, Backups, GCP
├── mods.yaml                     Modliste — Single Source of Truth
├── spawns.yaml                   Spawn-Regionen + eigene Coop-Spawns
├── CHANGELOG.md                  Versionshistorie des Setups
├── .github/workflows/ci.yml      CI: shellcheck + mods-validate + YAML-Check + Secret-Scan
├── backups/                      Lokale Backup-Ablage im Repo-Baum (.gitkeep-Platzhalter)
├── logs/                         Log-Ablage (.gitkeep-Platzhalter)
├── config/
│   ├── templates/                servertest.ini.tmpl — envsubst-Vorlage der Server-INI
│   ├── presets/sandbox/          coop-balanced.lua — Coop-Sandbox-Preset
│   ├── spawns/                   Spawn-Vorlagen (spawnregions, ravencreek)
│   └── generated/                Generator-Ausgabe (workshop-items.txt, spawns/) — entsteht erst beim Generieren
├── scripts/                      12 Bash-Skripte (announce, backup, healthcheck, install, monitor, notify, render-config, restore, mods-generate, mods-validate, spawns-generate, update) …
│   ├── lib/common.sh             … gemeinsame Bibliothek (env, log, notify, Guards)
│   └── windows/                  4 PowerShell-Gegenstücke (install, backup, update, monitor)
├── systemd/                      zomboid.service + backup/update service+timer + README
├── docker/                       docker-compose.yml + .env.docker.example (optionaler Betrieb)
├── gcp/                          Failover: create-instance.sh, startup-script.sh, sync-backups.sh, README
├── docs/                         10 deutsche Vertiefungs-Guides (01-architektur … 10-runbook-raven-creek)
└── servermods/README.md          Anleitung: eigene Mods im Workshop-Format entwickeln

Start- und Update-Kette

So hängen die Komponenten zur Laufzeit zusammen:

Server-Start (nach sudo systemctl start zomboid):

systemd (zomboid.service, Restart=always)
  → start-server.sh -servername ${SERVER_NAME} -Xmx${RAM_XMX}
    → Project-Zomboid-Serverprozess (liest ${PZ_DATA_DIR}/Server/${SERVER_NAME}.ini laut -servername; mit dem Repo-Default SERVER_NAME=servertest ist das die gerenderte servertest.ini — siehe Datenfluss (b))

Update-Zyklus (make update bzw. zomboid-update.timerscripts/update.sh):

announce.sh (RCON-Countdown)
  → Stop → backup.sh (Pre-Backup)
  → SteamCMD +app_update 380870
  → render-config.sh (Configs neu rendern)
  → Start → healthcheck.sh
  → bei FAIL: Auto-Rollback auf den Stand vor dem Update

Git-Hygiene

.env wird niemals committet (Repo-Regel: Secrets); die CI prüft bei jedem Push mit shellcheck die Skripte, validiert mods.yaml, prüft YAML und scannt auf Secrets (Repo-Inventar, .github/workflows/ci.yml). Jede Mod- oder Config-Änderung ist ein Commit und damit revertbar — die Wiki-Seiten Modding-Workflow und Updates-und-Rollback beschreiben die zugehörigen Abläufe.

Betriebsverzeichnisse

Was auf dem Linux-Host wo liegt (Defaults 1:1 aus .env.example und install.sh; alle Werte Repo-Default):

Variable Default-Pfad Inhalt
PZ_HOME /opt/pzserver Wurzel der Server-Installation; Heimat des dedizierten Users pzserver
PZ_SERVER_DIR /opt/pzserver/server Spiel-Binaries (per SteamCMD, App 380870) inkl. start-server.sh
PZ_DATA_DIR /opt/pzserver/Zomboid Zomboid-Datenverzeichnis: Saves, gerenderte Server-INIs, SandboxVars, Spawn-Lua (Server/-Unterordner)
STEAMCMD_DIR /opt/pzserver/steamcmd SteamCMD-Installation; Workshop-Downloads liegen darunter unter steamapps/workshop/content/108600/
BACKUP_DIR /opt/pzserver/backups tar.gz-Archive; Rotation 7 täglich / 4 wöchentlich (Repo-Default, RETENTION_DAILY/WEEKLY)

Failover-Kurzabriss

Als Notfallreserve läuft in GCP eine normalerweise gestoppte e2-standard-4-Instanz (Debian 12, 50 GB Disk — beides Repo-Default, gcp/create-instance.sh) neben einem Backup-Bucket in europe-west3; laufend kostet das rund $0.13/h (Community-Quelle: GCP-Preisliste — Näherung). Fällt der Haupt-Host aus, startet man die Instanz; ihr Startup-Skript installiert Docker und Git, klont das Repo und holt die täglichen daily-*.tar.gz aus dem Bucket nach /data/Zomboid, dann startet der Container mit SLOTS=3 und XMX=8G (Repo-Verhalten, gcp/startup-script.sh). Die Backups werden per gsutil rsync täglich per Cron auf dem Haupt-Host in den Bucket gespiegelt — gcp/README.md empfiehlt dafür z. B. 04:30, nach dem nächtlichen Backup. Vollständige Anleitung: GCP-Failover.

Weiter

Clone this wiki locally