- Astro 6 + React + Tailwind v4 Projekt-Skelett mit allen Marketing-Seiten (Home, Module, Tester, Souveränität, Roadmap, Kontakt, Impressum, Datenschutz) - Self-hosted Outfit + JetBrains Mono Fonts (DSGVO) - Marketing-Komponenten gemäss CLAUDE.md §5.6 (NumberedItem, ModuleCard, StatusDot, TechStrip, SovereigntyBlock, RoadmapTimeline, etc.) - Module-Daten in src/content/module.ts als Single Source of Truth - E2E Smoke-Tests via Playwright - OG-Image-Generator - Forgejo Workflow .forgejo/workflows/deploy.yml für Tier-2 Static Deploy - Infra-as-Code Snapshot in infra/marketing-vps/ - Brand-System Submodule auf Forgejo umgezogen (war GitHub) - Deployment- und Handoff-Dokumentation - .DS_Store aus Tracking entfernt, .gitignore um Test-Artefakte ergaenzt
549 lines
15 KiB
Markdown
549 lines
15 KiB
Markdown
# Deployment — SlimCore Marketing Site
|
|
|
|
> **Stand:** 2026-05-04 · **Zielgruppe:** Pascal Oelmann · **Adressat:** dieselbe Marketing-Site (`slimcore.io`) plus die anderen Marken-Sites (`digiformer.eu`, `slimsafe.io`, `fonboard.io`) als Folge-Schritte
|
|
> **Pfad:** Forgejo-First. GitHub wird übersprungen — kein Übergangs-Setup, das später migriert werden muss.
|
|
|
|
Dieses Dokument beschreibt wie die Marketing-Site live geht. Es ist Teil eines familienweiten Standards für **Tier 2 — Static Marketing-Sites** (siehe `docs/handoff.md` für die Tier-Definition).
|
|
|
|
---
|
|
|
|
## 0. Voraussetzungen — ein Mal pro Familie
|
|
|
|
Diese Phasen sind nur beim ersten Mal nötig. Spätere Marken-Sites laufen direkt zu Phase B.
|
|
|
|
### Phase A — Forgejo-Server + Runner (~1 h)
|
|
|
|
#### A.1 VPS bestellen
|
|
|
|
Hetzner Cloud → neuer Server in **Falkenstein**:
|
|
|
|
| Feld | Wert |
|
|
|---|---|
|
|
| Typ | CX22 (4 GB RAM / 2 vCPU / 40 GB SSD) |
|
|
| Image | Ubuntu 24.04 LTS |
|
|
| SSH-Key | dein Schlüssel + ed25519 (kein RSA) |
|
|
| Cloud-Init | optional — das Setup unten ist manuell |
|
|
| Backups | aktivieren (~€0,80/mo extra) |
|
|
| Standort | Falkenstein (FSN1) |
|
|
| Hostname | `forge.digiformer.eu` |
|
|
| Private Network | `slimcore-infra` (10.0.0.0/24) — falls später shared mit SlimCore-Cluster |
|
|
|
|
Kosten: ~€4/mo + €0,80 Backups.
|
|
|
|
#### A.2 Server härten (~10 min)
|
|
|
|
```bash
|
|
# SSH als root
|
|
ssh root@<forge-ip>
|
|
|
|
# System aktualisieren
|
|
apt update && apt upgrade -y && apt autoremove -y
|
|
|
|
# Deploy-User
|
|
adduser --disabled-password --gecos "" deploy
|
|
usermod -aG sudo,docker deploy
|
|
mkdir -p /home/deploy/.ssh
|
|
cp ~/.ssh/authorized_keys /home/deploy/.ssh/
|
|
chown -R deploy:deploy /home/deploy/.ssh
|
|
chmod 600 /home/deploy/.ssh/authorized_keys
|
|
|
|
# SSH härten — root-Login deaktivieren, nur Key-Auth
|
|
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
|
|
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
|
|
systemctl reload ssh
|
|
|
|
# Firewall
|
|
apt install -y ufw fail2ban
|
|
ufw default deny incoming
|
|
ufw default allow outgoing
|
|
ufw allow OpenSSH
|
|
ufw allow http
|
|
ufw allow https
|
|
ufw --force enable
|
|
systemctl enable --now fail2ban
|
|
|
|
# Docker installieren
|
|
curl -fsSL https://get.docker.com | sh
|
|
usermod -aG docker deploy
|
|
```
|
|
|
|
#### A.3 Forgejo + Caddy + Runner via Docker Compose
|
|
|
|
`/home/deploy/forgejo/docker-compose.yml`:
|
|
|
|
```yaml
|
|
services:
|
|
forgejo:
|
|
image: codeberg.org/forgejo/forgejo:9
|
|
container_name: forgejo
|
|
restart: unless-stopped
|
|
environment:
|
|
USER_UID: '1000'
|
|
USER_GID: '1000'
|
|
FORGEJO__database__DB_TYPE: postgres
|
|
FORGEJO__database__HOST: db:5432
|
|
FORGEJO__database__NAME: forgejo
|
|
FORGEJO__database__USER: forgejo
|
|
FORGEJO__database__PASSWD_FILE: /run/secrets/db_password
|
|
FORGEJO__server__DOMAIN: forge.digiformer.eu
|
|
FORGEJO__server__ROOT_URL: https://forge.digiformer.eu/
|
|
FORGEJO__server__SSH_DOMAIN: forge.digiformer.eu
|
|
FORGEJO__service__DISABLE_REGISTRATION: 'true'
|
|
FORGEJO__webhook__ALLOWED_HOST_LIST: 'private,*.slimcore.io,*.digiformer.eu'
|
|
secrets:
|
|
- db_password
|
|
volumes:
|
|
- ./data:/var/lib/gitea
|
|
- ./config:/etc/gitea
|
|
ports:
|
|
- '127.0.0.1:3000:3000'
|
|
- '0.0.0.0:2222:22'
|
|
depends_on:
|
|
- db
|
|
|
|
db:
|
|
image: postgres:16-alpine
|
|
container_name: forgejo-db
|
|
restart: unless-stopped
|
|
environment:
|
|
POSTGRES_DB: forgejo
|
|
POSTGRES_USER: forgejo
|
|
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
|
|
secrets:
|
|
- db_password
|
|
volumes:
|
|
- ./pg-data:/var/lib/postgresql/data
|
|
|
|
runner:
|
|
image: code.forgejo.org/forgejo/runner:6
|
|
container_name: forgejo-runner
|
|
restart: unless-stopped
|
|
user: '1000:1000'
|
|
volumes:
|
|
- ./runner-data:/data
|
|
- /var/run/docker.sock:/var/run/docker.sock
|
|
command: /bin/sh -c 'sleep 5; forgejo-runner daemon'
|
|
depends_on:
|
|
- forgejo
|
|
|
|
secrets:
|
|
db_password:
|
|
file: ./db_password.txt
|
|
```
|
|
|
|
```bash
|
|
# Auf dem Server
|
|
sudo -u deploy bash
|
|
cd /home/deploy/forgejo
|
|
mkdir -p data config pg-data runner-data
|
|
openssl rand -hex 24 > db_password.txt
|
|
chmod 600 db_password.txt
|
|
docker compose up -d
|
|
|
|
# Erste Schritte
|
|
docker compose logs -f forgejo
|
|
# Browser: http://<forge-ip>:3000 (über SSH-Tunnel oder kurz UFW öffnen)
|
|
# Setup-Wizard durchklicken: Admin-User anlegen, kein Email für jetzt
|
|
```
|
|
|
|
#### A.4 Caddy als TLS-Proxy
|
|
|
|
Auf demselben Server, separat:
|
|
|
|
`/home/deploy/caddy/docker-compose.yml`:
|
|
|
|
```yaml
|
|
services:
|
|
caddy:
|
|
image: caddy:2-alpine
|
|
container_name: caddy
|
|
restart: unless-stopped
|
|
ports:
|
|
- '80:80'
|
|
- '443:443'
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- caddy-data:/data
|
|
- caddy-config:/config
|
|
network_mode: host # damit Caddy localhost:3000 erreicht
|
|
|
|
volumes:
|
|
caddy-data:
|
|
caddy-config:
|
|
```
|
|
|
|
`/home/deploy/caddy/Caddyfile`:
|
|
|
|
```caddy
|
|
forge.digiformer.eu {
|
|
reverse_proxy 127.0.0.1:3000
|
|
encode zstd gzip
|
|
header Strict-Transport-Security "max-age=31536000"
|
|
}
|
|
```
|
|
|
|
```bash
|
|
docker compose up -d
|
|
# DNS A-Record forge.digiformer.eu → <forge-ip>
|
|
# Caddy holt automatisch Let's-Encrypt-Cert, ~30 s
|
|
```
|
|
|
|
#### A.5 Runner registrieren
|
|
|
|
In der Forgejo-UI (`https://forge.digiformer.eu`):
|
|
- **Site Administration → Actions → Runners → Create new Runner**
|
|
- Token kopieren
|
|
|
|
```bash
|
|
docker compose -f /home/deploy/forgejo/docker-compose.yml exec runner forgejo-runner register \
|
|
--no-interactive \
|
|
--instance https://forge.digiformer.eu \
|
|
--token <runner-token> \
|
|
--name forge-runner-1 \
|
|
--labels docker:docker://node:22-bookworm,ubuntu-latest:docker://node:22-bookworm
|
|
```
|
|
|
|
Verifikation: in Forgejo-UI sollte der Runner als **online** erscheinen. Damit ist die Forgejo-Plattform fertig.
|
|
|
|
#### A.6 Backup einrichten
|
|
|
|
Cron auf Forgejo-Server (User `deploy`):
|
|
|
|
```bash
|
|
# /home/deploy/backup-forgejo.sh
|
|
#!/bin/bash
|
|
set -euo pipefail
|
|
DATE=$(date +%Y-%m-%d-%H%M)
|
|
DEST="/home/deploy/backups/forgejo-$DATE.tar.zst"
|
|
docker exec forgejo gitea dump --type tar.zst --file - > "$DEST"
|
|
# Push to Storage Box (siehe Storage-Box-Setup unten)
|
|
rsync -e "ssh -p 23 -i /home/deploy/.ssh/storagebox" "$DEST" \
|
|
uXXXXXX@uXXXXXX.your-storagebox.de:/home/forgejo-backups/
|
|
# Lokale Retention: 7 Tage
|
|
find /home/deploy/backups -name "forgejo-*.tar.zst" -mtime +7 -delete
|
|
```
|
|
|
|
```bash
|
|
chmod +x /home/deploy/backup-forgejo.sh
|
|
crontab -e
|
|
# 0 3 * * * /home/deploy/backup-forgejo.sh >> /var/log/forgejo-backup.log 2>&1
|
|
```
|
|
|
|
---
|
|
|
|
## Phase B — Marketing-VPS einrichten (~30 min)
|
|
|
|
### B.1 VPS bestellen
|
|
|
|
Wieder Hetzner Cloud, gleiche Wahl wie A.1, aber Hostname `marketing.digiformer.eu`. Standort Falkenstein.
|
|
|
|
### B.2 Server härten
|
|
|
|
Identisch zu A.2 — Deploy-User, Firewall, Docker.
|
|
|
|
### B.3 Caddy installieren
|
|
|
|
Marketing-VPS hostet alle statischen Marken-Sites über einen einzigen Caddy.
|
|
|
|
`/home/deploy/marketing/docker-compose.yml`:
|
|
|
|
```yaml
|
|
services:
|
|
caddy:
|
|
image: caddy:2-alpine
|
|
container_name: marketing-caddy
|
|
restart: unless-stopped
|
|
ports:
|
|
- '80:80'
|
|
- '443:443'
|
|
volumes:
|
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
- ./sites:/var/www:ro
|
|
- caddy-data:/data
|
|
- caddy-config:/config
|
|
|
|
volumes:
|
|
caddy-data:
|
|
caddy-config:
|
|
```
|
|
|
|
`/home/deploy/marketing/Caddyfile`:
|
|
|
|
```caddy
|
|
# — slimcore.io —
|
|
slimcore.io, www.slimcore.io {
|
|
root * /var/www/slimcore.io
|
|
try_files {path} {path}/ /index.html
|
|
file_server
|
|
encode zstd gzip
|
|
header {
|
|
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
|
X-Content-Type-Options nosniff
|
|
Referrer-Policy strict-origin-when-cross-origin
|
|
-Server
|
|
}
|
|
# Astro generiert /index.html, /en/index.html, etc.
|
|
# Sitemap + robots gehen direkt
|
|
handle /sitemap-*.xml {
|
|
file_server
|
|
}
|
|
handle /robots.txt {
|
|
file_server
|
|
}
|
|
# SPA-Fallback nicht nötig — alle Routen haben echte HTML-Files
|
|
}
|
|
|
|
# — digiformer.eu — (analog, sobald migriert)
|
|
digiformer.eu, www.digiformer.eu {
|
|
root * /var/www/digiformer.eu
|
|
file_server
|
|
encode zstd gzip
|
|
header Strict-Transport-Security "max-age=31536000"
|
|
}
|
|
|
|
# — slimsafe.io —
|
|
slimsafe.io, www.slimsafe.io {
|
|
root * /var/www/slimsafe.io
|
|
file_server
|
|
encode zstd gzip
|
|
header Strict-Transport-Security "max-age=31536000"
|
|
}
|
|
|
|
# — fonboard.io —
|
|
fonboard.io, www.fonboard.io {
|
|
root * /var/www/fonboard.io
|
|
file_server
|
|
encode zstd gzip
|
|
header Strict-Transport-Security "max-age=31536000"
|
|
}
|
|
|
|
# Catch-all 404 für unbekannte Hostnames
|
|
:80 {
|
|
respond 404
|
|
}
|
|
```
|
|
|
|
```bash
|
|
sudo -u deploy bash
|
|
cd /home/deploy/marketing
|
|
mkdir -p sites/slimcore.io sites/digiformer.eu sites/slimsafe.io sites/fonboard.io
|
|
docker compose up -d
|
|
```
|
|
|
|
### B.4 SSH-Key für Forgejo-Runner → Marketing-VPS
|
|
|
|
Forgejo's Self-hosted Runner muss per SSH auf den Marketing-VPS rsynce können.
|
|
|
|
```bash
|
|
# Auf Forgejo-Server (deploy-user)
|
|
ssh-keygen -t ed25519 -f ~/.ssh/marketing_deploy_key -N ""
|
|
cat ~/.ssh/marketing_deploy_key.pub
|
|
```
|
|
|
|
```bash
|
|
# Auf Marketing-VPS (deploy-user)
|
|
adduser --disabled-password --gecos "" rsync-deploy
|
|
mkdir -p /home/rsync-deploy/.ssh
|
|
echo "<public-key-vom-forgejo-server>" >> /home/rsync-deploy/.ssh/authorized_keys
|
|
# Restriktion: nur rsync, kein Shell-Login
|
|
echo 'command="rrsync -wo /var/www" <public-key>' > /home/rsync-deploy/.ssh/authorized_keys
|
|
chmod 700 /home/rsync-deploy/.ssh
|
|
chmod 600 /home/rsync-deploy/.ssh/authorized_keys
|
|
chown -R rsync-deploy:rsync-deploy /home/rsync-deploy/.ssh
|
|
# rsync-Schreibrechte auf /var/www
|
|
mkdir -p /var/www
|
|
chown -R rsync-deploy:rsync-deploy /var/www
|
|
```
|
|
|
|
In Forgejo-UI für das `slimcore-website`-Repo:
|
|
- **Settings → Secrets and Variables → Actions → New Secret**:
|
|
- `MARKETING_SSH_KEY`: privater Schlüsselinhalt von `~/.ssh/marketing_deploy_key` auf Forgejo-Server
|
|
- `MARKETING_HOST`: IP oder DNS des Marketing-VPS
|
|
- `MARKETING_USER`: `rsync-deploy`
|
|
|
|
---
|
|
|
|
## Phase C — Slimcore-Website Repo deployen
|
|
|
|
### C.1 Repo nach Forgejo migrieren
|
|
|
|
```bash
|
|
# Lokal, im slimcore-website-Folder
|
|
git remote add forgejo git@forge.digiformer.eu:digiformer/slimcore-website.git
|
|
git push --mirror forgejo
|
|
# Damit ist alles drüben — Branches, Tags, History
|
|
```
|
|
|
|
In Forgejo-UI:
|
|
- Repo `digiformer/slimcore-website` ist nun gespiegelt
|
|
- **Repository → Settings → Mirror Settings**: aus, weil unsere Quelle jetzt Forgejo ist
|
|
|
|
### C.2 Workflow-Datei
|
|
|
|
In diesem Repo bereits vorhanden: `.forgejo/workflows/deploy.yml` (siehe diese Datei für Details). Der Workflow wird automatisch erkannt, sobald das Repo in Forgejo liegt.
|
|
|
|
Trigger:
|
|
- `push` auf `main` → Test, Build, Deploy auf Marketing-VPS unter `slimcore.io`
|
|
|
|
### C.3 DNS-Records setzen
|
|
|
|
Beim Domain-Registrar (idealerweise INWX, Brand-System §7.3 Greylist):
|
|
|
|
| Record | Typ | Ziel |
|
|
|---|---|---|
|
|
| `slimcore.io` | A | `<marketing-vps-ip>` |
|
|
| `www.slimcore.io` | A | `<marketing-vps-ip>` |
|
|
| `digiformer.eu` (später) | A | `<marketing-vps-ip>` |
|
|
| `forge.digiformer.eu` | A | `<forge-vps-ip>` |
|
|
| `marketing.digiformer.eu` | A | `<marketing-vps-ip>` (für SSH-Zugriff) |
|
|
|
|
Caddy holt sich beim ersten Request das Let's-Encrypt-Cert automatisch. ~30 s nach DNS-Auflösung.
|
|
|
|
### C.4 Erstes Deployment auslösen
|
|
|
|
```bash
|
|
git checkout main
|
|
git push forgejo main
|
|
```
|
|
|
|
In Forgejo-UI:
|
|
- **Actions** Tab im Repo → laufender Workflow
|
|
- Bei Erfolg ist `https://slimcore.io/` live
|
|
|
|
### C.5 Verifikation
|
|
|
|
Auf einem beliebigen Rechner:
|
|
|
|
```bash
|
|
curl -I https://slimcore.io/
|
|
# → 200 OK, HSTS-Header, server: Caddy
|
|
|
|
curl https://slimcore.io/sitemap-index.xml | head
|
|
# → Sitemap-XML
|
|
|
|
curl https://slimcore.io/robots.txt
|
|
# → robots.txt
|
|
|
|
curl -I https://slimcore.io/en/
|
|
# → 200 OK
|
|
```
|
|
|
|
Plus: lokale Playwright-Tests gegen Production einmal laufen lassen:
|
|
|
|
```bash
|
|
PLAYWRIGHT_BASE_URL=https://slimcore.io pnpm exec playwright test --grep "renders"
|
|
```
|
|
|
|
Falls alle 16 Render-Tests grün → Site ist produktionsreif.
|
|
|
|
---
|
|
|
|
## Phase D — Spätere Marken-Sites
|
|
|
|
Sobald Phase A + B abgeschlossen, ist jede weitere Marketing-Site:
|
|
|
|
1. Repo in Forgejo anlegen / migrieren
|
|
2. `.forgejo/workflows/deploy.yml` aus diesem Repo kopieren, `slimcore.io` durch passende Domain ersetzen
|
|
3. DNS A-Record auf Marketing-VPS-IP zeigen
|
|
4. Caddyfile-Block auf Marketing-VPS ergänzen + `caddy reload`
|
|
|
|
Das ist ~15 min pro Marke. Kein neues VPS, kein neues Forgejo-Setup.
|
|
|
|
---
|
|
|
|
## Phase E — SlimCore-App-Repo (cockpit) später migrieren
|
|
|
|
Cockpit folgt demselben Prinzip aber komplexer (3 Production-VPS, Branch-Protection, secrets-Repo, Patroni-Migrations). Eigener Migrations-Plan in `cockpit/docs/infrastructure/forgejo-migration.md` — wird erst angegangen, wenn Marketing-Site stabil läuft. Forgejo + Runner sind dann schon da; nur noch Workflow-Portierung + Secrets-Re-Upload + Branch-Protection.
|
|
|
|
---
|
|
|
|
## Backup-Strategie pro Komponente
|
|
|
|
| Was | Wie | Wohin | Frequenz |
|
|
|---|---|---|---|
|
|
| Forgejo-Daten (Repos + DB + Config) | `gitea dump` → tar.zst | Hetzner Storage Box Nürnberg | Täglich 03:00 |
|
|
| Marketing-Site `/var/www/*` | Inhaltlich = Build-Output, immer aus Git wieder herstellbar | — | nicht nötig |
|
|
| Caddy-Daten (Let's-Encrypt-Certs) | Volume-Snapshot | Storage Box | Wöchentlich |
|
|
| Marketing-VPS-Config (`docker-compose.yml`, `Caddyfile`) | Im Forgejo-Repo `infra/marketing-vps/` | — | bei jedem Commit |
|
|
|
|
**Storage Box:** Hetzner Storage Box BX21 (1 TB, Nürnberg, ~€10/mo). Ein gemeinsamer Account für alle Marken. SFTP/rsync-Zugriff. Sub-Pfade pro System: `/forgejo-backups/`, `/slimcore-app-backups/`, etc.
|
|
|
|
---
|
|
|
|
## Monitoring (vorerst minimal)
|
|
|
|
Phase 1 reicht **Uptime Kuma** auf demselben Marketing-VPS:
|
|
|
|
```yaml
|
|
# /home/deploy/uptime-kuma/docker-compose.yml
|
|
services:
|
|
uptime:
|
|
image: louislam/uptime-kuma:1
|
|
container_name: uptime
|
|
restart: unless-stopped
|
|
ports:
|
|
- '127.0.0.1:3001:3001'
|
|
volumes:
|
|
- ./data:/app/data
|
|
```
|
|
|
|
Caddy-Block dafür:
|
|
|
|
```caddy
|
|
status.digiformer.eu {
|
|
reverse_proxy 127.0.0.1:3001
|
|
basicauth {
|
|
pascal $2a$14$<bcrypt-hash> # caddy hash-password generieren
|
|
}
|
|
}
|
|
```
|
|
|
|
Monitore einrichten: HTTPS-Check für jede Marken-Site + Forgejo. Bei Ausfall Mail an Pascal über Brevo SMTP.
|
|
|
|
---
|
|
|
|
## Disaster Recovery — Wenn der Marketing-VPS ausfällt
|
|
|
|
Marketing-Site ist statisch und reproduzierbar:
|
|
|
|
1. **Neuer VPS bestellen** (Hetzner UI, ~5 min)
|
|
2. Server härten + Caddy installieren (Phase B.2 + B.3, ~20 min)
|
|
3. DNS A-Records auf neue IP umstellen (Hetzner DNS oder Registrar, ~5 min)
|
|
4. Forgejo-Workflow manuell triggern (Forgejo-UI → Actions → Run workflow) für jede Marken-Site (~5 min Build + Deploy pro Site)
|
|
|
|
**Total RTO: ~45 min.** Kein RPO-Datenverlust, weil Build-Output reproduzierbar ist.
|
|
|
|
Wenn der **Forgejo-VPS** ausfällt:
|
|
|
|
1. Neuer VPS bestellen
|
|
2. Backup von Storage Box ziehen (`gitea dump`)
|
|
3. Forgejo-Container hochfahren mit dump-restore
|
|
4. Runner neu registrieren
|
|
|
|
**Total RTO: ~1 h.** RPO: bis 24 h (letztes Backup) — aber lokale Git-Clones haben aktuelle Stände, falls nötig push-zurück.
|
|
|
|
---
|
|
|
|
## Kostenbilanz Tier-2-Setup
|
|
|
|
| Posten | €/mo |
|
|
|---|---|
|
|
| Forgejo-VPS CX22 + Backups | ~5 |
|
|
| Marketing-VPS CX22 + Backups | ~5 |
|
|
| Hetzner Storage Box BX21 (1 TB) — geteilt mit anderen Backups | ~10 |
|
|
| Domain-Registrar (INWX o.ä.) | ~1/Domain/Jahr |
|
|
| **Total Marketing-Infra** | **~20/mo** |
|
|
|
|
Das ist fix für **alle** Marketing-Sites — egal ob 1 oder 10 Marken.
|
|
|
|
---
|
|
|
|
## Was als Nächstes
|
|
|
|
- [ ] Pascal: VPS bestellen (Forgejo + Marketing)
|
|
- [ ] Phase A durcharbeiten — Forgejo + Runner laufen, TLS aktiv
|
|
- [ ] Phase B durcharbeiten — Marketing-VPS bereit für rsync
|
|
- [ ] Phase C durcharbeiten — `slimcore-website` deployt unter `https://slimcore.io/`
|
|
- [ ] Smoke-Test, dann live
|
|
- [ ] Andere Marken-Sites einzeln nach Phase D nachziehen
|
|
- [ ] Cockpit-Migration als separater Plan später (Phase E)
|