· DevOps  · 4 Min. Lesezeit

Statische Sites ohne CI-Cloud deployen: meine Ein-Befehl-Pipeline

Wie ich Astro-Seiten auf einen eigenen Server bringe, lokal gebaut und in Sekunden umgeschaltet, ohne Netlify oder Vercel. Mit den Fallen, die dabei wirklich aufgetaucht sind, und ihren Lösungen.

Wie ich Astro-Seiten auf einen eigenen Server bringe, lokal gebaut und in Sekunden umgeschaltet, ohne Netlify oder Vercel. Mit den Fallen, die dabei wirklich aufgetaucht sind, und ihren Lösungen.

Für statische Seiten braucht es keinen SaaS-Deploy. Ein Astro- oder Hugo-Build ist am Ende ein Ordner mit fertigem HTML, und den kann man selbst ausliefern. Ich deploye meine Seiten mit einem einzigen Skript auf einen eigenen Server, lokal gebaut und ohne Downtime umgeschaltet. So sieht die Pipeline aus, und so sehen die Stolpersteine aus, über die ich dabei gefallen bin.

Der Ablauf in einem Befehl

Das Deploy-Skript macht immer dasselbe:

  1. Die Quelle in einen lokalen Wegwerf-Ordner spiegeln (nicht auf ein Netzlaufwerk).
  2. npm ci nur dann laufen lassen, wenn das Lockfile neuer ist als node_modules.
  3. Mit npm run build den statischen Ordner erzeugen.
  4. Den dist/-Ordner als tar packen und per scp auf den Server schieben.
  5. Serverseitig den Inhalt des ausgelieferten Ordners ersetzen.
  6. Mit einem curl auf HTTP 200 prüfen, dass die Seite lebt.

Ich nutze bewusst npm ci statt npm install, weil es sauber aus dem Lockfile installiert und dafür gedacht ist, den node_modules-Ordner reproduzierbar neu aufzubauen (npm-Doku zu npm ci). Die Kernschritte des Skripts sehen so aus:

rsync -a --delete ./ "$TMP/"
( cd "$TMP" && npm ci && npm run build )
tar -C "$TMP/dist" -czf site.tgz .
scp site.tgz deploy@server:/tmp/

Der Build läuft bewusst in einem lokalen Temp-Ordner mit einem trap 'rm -rf "$TMP"' EXIT, nicht direkt auf einem gemounteten Share. Auf dem Share war der Build lahm und fehleranfällig, lokal ist er schnell und lässt keine Datenleiche zurück.

Danach ersetze ich auf dem Server den ausgelieferten Inhalt und prüfe die Seite. An genau diesen Schritten sind mir die folgenden Fallstricke begegnet.

Fallstrick 1: nginx serviert den alten Stand

Der naive Weg, den Zielordner zu löschen und neu anzulegen, bricht bei einem Bind-Mount den Inode, und nginx liefert weiter die alten Dateien aus. Die Lösung ist, den Ordner nicht zu ersetzen, sondern seinen Inhalt:

find /srv/site -mindepth 1 -delete
cp -a /tmp/site.new/. /srv/site/

Damit bleibt der Mount-Inode erhalten, und die Seite wechselt ohne Container-Neustart und ohne Downtime auf den neuen Stand. Wie nginx Dateien aus dem Wurzelverzeichnis ausliefert, steht in der nginx-Dokumentation.

Fallstrick 2: 403 auf Assets

Nach dem ersten Deploy kamen die Dateien aus public/ mit zu strengen Rechten an, nginx antwortete mit 403. Ein find-Zweizeiler stellt die üblichen Rechte wieder her:

find /srv/site -type d -exec chmod 755 {} +
find /srv/site -type f -exec chmod 644 {} +

Fallstrick 3: macOS schmuggelt Metadaten mit

Ein auf dem Mac gepacktes tar schleppt AppleDouble-Dateien (._*) und .DS_Store mit, die dann im ausgelieferten Ordner landen. Ich entferne ._* und .DS_Store vor dem Packen und packe das tar dann so, dass keine erweiterten Attribute mitkommen:

COPYFILE_DISABLE=1 tar --no-xattrs -C "$TMP/dist" -czf site.tgz .

Fallstrick 4: Config-Änderung greift nicht

Ändere ich eine einzeln gemountete Datei (etwa die nginx.conf) auf dem Server, greift sie nicht automatisch, auch nicht mit watch. Ein Single-File-Bind-Mount hängt am Inode der Ursprungsdatei. Nach solchen Änderungen hilft nur, den betroffenen Dienst neu zu erzeugen:

docker compose up -d --force-recreate <dienst>

Das --force-recreate-Flag zwingt Compose, den Container neu anzulegen, auch wenn sich Image und Konfiguration scheinbar nicht geändert haben (Docker-Doku zu docker compose up).

Ehrlich: Wann sich eine echte CI lohnt

Diese Pipeline ist ideal für ein bis wenige Seiten, die eine Person pflegt. Sobald mehrere Leute parallel committen, Vorschau-Deploys pro Branch gebraucht werden oder Tests im Build laufen sollen, ist eine richtige CI/CD-Strecke das bessere Werkzeug. Für den souveränen Betrieb weniger Seiten auf eigenem Server ist der Ein-Befehl-Weg schlank, nachvollziehbar und ohne Fremd-Abhängigkeit.

Sie wollen Ihre Seiten auf eigenem Server statt in einer fremden Cloud betreiben? Ich richte eine schlanke, nachvollziehbare Deploy-Strecke ein, die Ihnen gehört. Wie ich Infrastruktur souverän aufsetze, lesen Sie unter Infrastruktur & Cloud, das Erstgespräch ist unverbindlich.

Alex Jabi, Jabi IT

Alex Jabi

Ich betreue IT, Informationssicherheit und Datenschutz für KMU und Praxen an der Bergstraße und im Odenwald, persönlich, dokumentiert und ohne Cloud-Zwang.

Mehr über mich
Zurück zum Blog

Passende Artikel

Alle Artikel »