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
283 lines
9.8 KiB
Markdown
283 lines
9.8 KiB
Markdown
# 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'`).
|