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

314 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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:
```js
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`).
```dockerfile
# --- 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`)
```nginx
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`:
```bash
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-website` → **Einstellungen →
Deploy-Schlüssel → Schlüssel hinzufügen**, einfügen, **schreibgeschützt** lassen.
### B2. Repo auf den Server klonen (einmalig)
```bash
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:
```yaml
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
```caddyfile
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
```bash
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)
```bash
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.mjs``site: '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):
```bash
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)
```bash
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/keys`
**Deploy-Schlüssel hinzufügen**, einfügen, **schreibgeschützt** lassen.
### B2 Repo klonen
```bash
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):
```yaml
website:
build:
context: /home/deploy/sites/larpaso-website
restart: unless-stopped
networks: [web]
```
### B4 Caddy-Block in `~/stack/caddy/Caddyfile` ergänzen
```caddyfile
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
```bash
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
```bash
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'`).