larpaso-website/DEPLOYMENT.md
Pascal Scholz ec872efa42 Document live deployment status and future update workflow
larpaso.de is now confirmed live (verified 200 OK + valid TLS cert).
Records what was done for Teil B and clarifies the ongoing workflow:
Claude Code can edit/build/push code changes directly, but the
server-side rebuild step still needs to run on the server manually
unless an auto-deploy hook is set up later.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8eccP4XnXFJvpSYMm26as
2026-09-12 04:01:17 +02:00

12 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


Status: LIVE (Stand 2026-09-12)

Teil A im Repo, geprüft:

  • astro.config.mjssite: 'https://larpaso.de', statischer Output (Standard, kein Adapter).
  • Dockerfile, nginx.conf, .dockerignore liegen im Repo, entsprechen exakt der Vorlage oben.
  • Lokaler npm run build läuft fehlerfrei durch (4 statische Seiten, dist/ wird erzeugt).

Teil B auf dem Server, durchgeführt von Pascal:

  • Deploy-Key erzeugt, Repo nach ~/sites/larpaso-website geklont.
  • website-Service in ~/stack/docker-compose.yml ergänzt (Netzwerk web, kein Host-Port).
  • Caddy-Block für larpaso.de + www.larpaso.de in ~/stack/caddy/Caddyfile ergänzt.
  • DNS bei IONOS: A-Eintrag @152.53.118.4 (Server-IP), alter AAAA-Eintrag entfernt.
  • docker compose build website && up -d website && restart caddy ausgeführt.
  • Verifiziert: https://larpaso.de liefert 200 OK, gültiges Let's-Encrypt-Zertifikat, www.larpaso.de leitet korrekt weiter (301).

Bekannter Nicht-Blocker: DNS-Propagierung kann bei manchen Resolvern (z. B. lokale Fritz!Box-Caches) noch etwas nachhinken kein Handlungsbedarf, löst sich von selbst.


Künftige Änderungen an der Seite

Ablauf für alles, was Code/Inhalte im Repo betrifft (Texte, neue Seiten, Design, etc.):

  1. Im Chat beschreiben, was geändert werden soll.
  2. Claude Code ändert den Code lokal, testet den Build (npm run build) und pusht nach git.larpaso.de/pascal/larpaso-website (main-Branch) das kann ich direkt selbst.
  3. Auf dem Server muss danach einmal die neue Version gebaut/gestartet werden (B7):
    cd ~/sites/larpaso-website && git pull
    cd ~/stack && docker compose build website && docker compose up -d website
    
    Diesen letzten Schritt musst du selbst ausführen ich habe keinen SSH-Zugriff auf den Server (bewusst so eingerichtet, siehe Sicherheits-Leitplanken oben).

Falls das lästig wird: Wir können optional eine Auto-Deploy-Automatisierung einrichten (z. B. ein Forgejo-Actions-Workflow oder ein einfacher Webhook auf dem Server), der bei jedem Push automatisch B7 ausführt. Dann reicht wirklich nur noch der Chat mit mir. Sag Bescheid, falls du das willst dafür bräuchte ich dann kurz Zugriff/Bestätigung für die Einrichtung auf dem Server (einmalig, durch dich ausgeführt).

Teil B Referenz (bereits erledigt, hier nur als Nachschlagewerk):

B1 Deploy-Key erzeugen (als deploy auf dem Server)

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

Den ausgegebenen Public Key in Forgejo eintragen: https://git.larpaso.de/pascal/larpaso-website/settings/keysDeploy-Schlüssel hinzufügen, einfügen, schreibgeschützt lassen.

B2 Repo klonen

mkdir -p ~/sites
GIT_SSH_COMMAND="ssh -i ~/.ssh/id_ed25519_larpaso-website" \
  git clone ssh://git@git.larpaso.de:2222/pascal/larpaso-website.git ~/sites/larpaso-website

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

Unter services: einfügen (Einrückung an bestehende Datei anpassen):

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

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

larpaso.de {
    reverse_proxy website:80
}

www.larpaso.de {
    redir https://larpaso.de{uri} permanent
}

(Den www-Block nur, wenn auch ein DNS-A-Eintrag für www existiert.)

B5 DNS bei IONOS

  • A-Eintrag @ → Server-IPv4 (bestehenden @-Eintrag ersetzen).
  • Optional A-Eintrag www → Server-IPv4.
  • Prüfen: nslookup larpaso.de muss die Server-IP liefern, bevor B6 läuft.

B6 Bauen & starten

cd ~/stack
docker compose build website
docker compose up -d website
docker compose restart caddy
docker compose logs -f caddy   # auf "certificate obtained successfully" für larpaso.de warten

Danach https://larpaso.de im Browser prüfen.

B7 Künftige Updates

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').