larpaso-website/DEPLOYMENT.md
Pascal Scholz 28ba23102a Add Docker deployment setup for static Astro site
Multi-stage Dockerfile (node build -> nginx serve), nginx config with
_astro asset caching, .dockerignore, and .gitignore hardening for
TLS key files. Astro already builds static output with site set to
https://larpaso.de.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JmTVcwbGJBYP7gu4P7b1yG
2026-09-12 03:21:47 +02:00

7.5 KiB
Raw Blame History

DEPLOYMENT.md Kontext & Aufgabe für Claude Code

Dieses Dokument gibt dir (Claude Code) den nötigen Kontext, um die Agentur-Website sauber auf dem bestehenden VPS auszuliefern, ohne mit der laufenden Infrastruktur zu kollidieren. Lies es komplett, bevor du Änderungen vornimmst.

Es gibt zwei Teile:

  • Teil A im Projekt (das machst du, Claude Code): Dateien im Repo erstellen, committen, pushen.
  • Teil B auf dem Server (das macht der Mensch, Pascal, selbst): nur Referenz, damit du die Dateien passend dazu erstellst. Führe Teil B NICHT aus und frage nicht nach Server-Zugängen.

1. Bestehende Infrastruktur (nicht verändern!)

Auf dem Server läuft bereits ein Docker-Compose-Stack. Wichtig für dich: Es gibt schon einen zentralen Reverse Proxy (Caddy), der Ports 80/443 besitzt und per Domain verteilt. Du darfst keinen zweiten Reverse Proxy bauen und keine Ports 80/443 belegen.

Aufbau (Ordner ~/stack auf dem Server = /home/deploy/stack):

  • docker-compose.yml mit den Diensten: caddy, nextcloud, nextcloud-db (MariaDB), redis, nextcloud-cron, forgejo.
  • Zwei Docker-Netzwerke: web (alles, was Caddy erreichen soll) und internal (Datenbank etc.).
  • caddy/Caddyfile verteilt nach Hostname:
    • cloud.larpaso.de → Nextcloud
    • git.larpaso.de → Forgejo
  • SSL/HTTPS macht Caddy vollautomatisch über Let's Encrypt. Neue Domains brauchen nur einen Caddy-Block + einen passenden DNS-A-Eintrag.

Regeln:

  • Bestehende Dienste (nextcloud*, forgejo, *-db, redis, caddy) nicht anfassen/neu starten.
  • Der neue Website-Dienst hängt nur am Netzwerk web (nicht internal).
  • Der Website-Container exponiert keine Host-Ports. Caddy erreicht ihn intern über den Container-Namen im web-Netzwerk.
  • Keine Passwörter, Tokens, Keys oder Inhalte der .env ins Repo, ins Dockerfile oder in Chat-Ausgaben schreiben.

2. Ziel

Die Astro-Website soll unter https://larpaso.de live gehen (optional zusätzlich www.larpaso.de → Weiterleitung auf larpaso.de). Sie wird auf dem Server gebaut (Multi-Stage-Docker-Build: Node baut die statischen Dateien, nginx liefert sie aus) und über den bestehenden Caddy ausgeliefert.


TEIL A Deine Aufgaben im Projekt (Claude Code)

A1. Projekt prüfen

  • Zeig die Projektstruktur der obersten Ebene.
  • Bestätige: Ist es ein Astro-Projekt? Gibt es package.json (und Lockfile)?
  • Ermittle den Paketmanager (npm / pnpm / yarn) am Lockfile (package-lock.json → npm, pnpm-lock.yaml → pnpm, yarn.lock → yarn) und die passenden Build-Befehle. Passe die Dockerfile-Befehle unten entsprechend an.

A2. Astro auf statische Ausgabe + Domain konfigurieren

  • Stelle sicher, dass Astro statisch baut (Standard output: 'static' KEIN Server-Adapter, da nginx nur statische Dateien ausliefert).
  • Setze in astro.config.* die Site-URL:
    export default defineConfig({
      site: 'https://larpaso.de',
      // output: 'static' ist Standard  nicht auf 'server' stellen
    });
    

A3. Multi-Stage-Dockerfile anlegen (Repo-Wurzel: Dockerfile)

Vorlage für npm. Bei pnpm/yarn die Install-/Build-Zeilen entsprechend anpassen (z. B. corepack enable && pnpm install --frozen-lockfile && pnpm build).

# --- Build-Stage ---
FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# --- Serve-Stage ---
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

A4. nginx-Konfiguration anlegen (Repo-Wurzel: nginx.conf)

server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    # Saubere URLs + Fallback
    location / {
        try_files $uri $uri/ $uri.html /index.html;
    }

    # Astro legt gehashte Assets unter /_astro ab -> lange cachen
    location /_astro/ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

A5. .dockerignore anlegen (Repo-Wurzel)

node_modules
dist
.git

A6. .gitignore sicherstellen (Repo-Wurzel)

Muss mindestens enthalten:

node_modules
dist

A7. Lokal gegenprüfen (optional, aber empfohlen)

  • Wenn Node lokal vorhanden: einmal npm install + npm run build ausführen und melden, ob der Build fehlerfrei einen dist-Ordner erzeugt. So fängst du Build-Fehler VOR dem Server-Deploy ab. (Falls Node lokal fehlt: überspringen und dem Menschen mitteilen.)

A8. Committen & pushen

  • git status prüfen: Es dürfen nur Quellcode + die neuen Konfigdateien (Dockerfile, nginx.conf, .dockerignore, .gitignore, astro.config.*) mitgehen kein node_modules, kein dist.
  • Sinnvolle Commit-Nachricht, dann git push origin main.
  • Danach dem Menschen bestätigen, dass alles in Forgejo liegt, und ihn auf Teil B verweisen.

TEIL B Server-Schritte (macht Pascal selbst; hier nur zur Abstimmung)

Diese Schritte führt der Mensch auf dem Server aus. Sie sind hier dokumentiert, damit die Dateien aus Teil A dazu passen. Claude Code führt sie nicht aus.

B1. Lese-Zugang des Servers zu Forgejo (einmalig)

Auf dem Server als deploy:

ssh-keygen -t ed25519 -C "server-deploy@larpaso" -f ~/.ssh/id_ed25519
cat ~/.ssh/id_ed25519.pub

Diesen öffentlichen Key in Forgejo eintragen: Repo larpaso-websiteEinstellungen → Deploy-Schlüssel → Schlüssel hinzufügen, einfügen, schreibgeschützt lassen.

B2. Repo auf den Server klonen (einmalig)

mkdir -p ~/sites
git clone ssh://git@git.larpaso.de:2222/pascal/larpaso-website.git ~/sites/larpaso-website

B3. Website-Dienst in ~/stack/docker-compose.yml ergänzen

Unter services: einfügen:

  website:
    build:
      context: /home/deploy/sites/larpaso-website
    restart: unless-stopped
    networks: [web]

(Keine ports:-Angabe Caddy erreicht den Container intern.)

B4. Caddy-Block in ~/stack/caddy/Caddyfile ergänzen

larpaso.de {
    reverse_proxy website:80
}

# optional, nur falls DNS-A-Eintrag für www existiert:
www.larpaso.de {
    redir https://larpaso.de{uri} permanent
}

B5. DNS bei IONOS setzen

  • A-Eintrag: Hostname @ → Server-IPv4 (den evtl. vorhandenen alten @-A-Eintrag ersetzen, nicht doppelt anlegen).
  • Optional A-Eintrag: Hostname www → Server-IPv4 (nur nötig, wenn der www-Block genutzt wird).
  • Hinweis: Ein A-Eintrag auf @ ändert die MX-/Mail-Einträge NICHT die E-Mails laufen unverändert weiter.
  • Vor dem nächsten Schritt kurz warten/prüfen: nslookup larpaso.de muss die Server-IP zeigen, sonst kann Caddy kein Zertifikat holen.

B6. Bauen & starten

cd ~/stack
docker compose build website
docker compose up -d website
docker compose restart caddy      # lädt den neuen Caddyfile-Block
docker compose logs -f caddy      # auf "certificate obtained successfully" für larpaso.de achten

Dann https://larpaso.de im Browser aufrufen.

B7. Künftige Updates (Ablauf bei jeder Änderung)

cd ~/sites/larpaso-website && git pull
cd ~/stack && docker compose build website && docker compose up -d website

Sicherheits-Leitplanken (Zusammenfassung)

  • Keine Secrets ins Repo/Dockerfile/Logs.
  • Kein zweiter Reverse Proxy, keine Belegung von 80/443 durch den Website-Dienst.
  • Website nur im web-Netzwerk, keine Host-Ports.
  • Bestehende Dienste nicht verändern oder neu starten (außer dem bewussten caddy restart in B6).
  • Astro bleibt statisch (output: 'static').