larpaso-website/DEPLOYMENT.md
Pascal Scholz e688eaa265 Add ready-to-run copy-paste blocks for server deployment (Teil B)
Documents that Teil A is verified (build tested locally) and provides
exact commands for deploy key, docker-compose service, Caddyfile
block, and DNS steps so the server-side rollout needs no improvisation.

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

283 lines
9.8 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
**Teil A ist erledigt und 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).
- Alles committet und nach `https://git.larpaso.de/pascal/larpaso-website` gepusht.
**Teil B steht noch aus** das macht Pascal selbst auf dem Server. Fertige Copy-Paste-Blöcke dafür:
### 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'`).