From 92820c251cfb88e7b94e4cdce201d11e6319c4c4 Mon Sep 17 00:00:00 2001 From: "XPS\\Micro" Date: Sun, 1 Feb 2026 21:28:00 +0100 Subject: [PATCH] docs: add comprehensive custom templates guide - Full guide for creating custom user templates - Step-by-step instructions with requirements - 3 complete examples: Python Flask, Hugo static site, Node.js with DB - Deployment workflows for local and production - Troubleshooting section and best practices - Quick reference and checklist --- docs/guides/custom-templates.md | 910 ++++++++++++++++++++++++++++++++ 1 file changed, 910 insertions(+) create mode 100644 docs/guides/custom-templates.md diff --git a/docs/guides/custom-templates.md b/docs/guides/custom-templates.md new file mode 100644 index 0000000..b78f89d --- /dev/null +++ b/docs/guides/custom-templates.md @@ -0,0 +1,910 @@ +# Custom Templates - Vollständige Anleitung + +## 📋 Inhaltsverzeichnis + +1. [Überblick](#überblick) +2. [Template-System Architektur](#template-system-architektur) +3. [Anforderungen an Templates](#anforderungen-an-templates) +4. [Neues Template erstellen](#neues-template-erstellen) +5. [Template-Beispiele](#template-beispiele) +6. [Deployment](#deployment) +7. [Troubleshooting](#troubleshooting) +8. [Best Practices](#best-practices) + +--- + +## Überblick + +Das Container Spawner System verwendet ein **dynamisches Template-System**, das beliebig viele User-Templates unterstützt: + +- **Automatische Erkennung**: `install.sh` findet und baut alle `user-template-*` Verzeichnisse +- **Flexible Konfiguration**: Templates werden in `.env` definiert (semikolon-getrennt) +- **Metadaten-Driven**: Display-Namen und Beschreibungen kommen aus `templates.json` +- **Multi-Container Support**: Jeder User kann beliebig viele Container verschiedener Typen erstellen + +**Standardtemplates:** +- `template-01`: Nginx Basic - Einfacher statischer Webserver +- `template-02`: Nginx Advanced - Nginx mit erweiterten Features +- `template-next`: Next.js Production - React-App mit Shadcn/UI + +**Custom Templates hinzufügen:** Beliebig viele eigene Templates erstellen (Python, Node.js, etc.) + +--- + +## Template-System Architektur + +### Wie das System funktioniert + +``` +1. install.sh durchsucht Verzeichnis + ↓ +2. Findet alle user-template-* Ordner + ↓ +3. Baut Docker Images automatisch + ↓ +4. Backend lädt Template-Liste aus .env + ↓ +5. Metadaten werden aus templates.json geladen + ↓ +6. Dashboard zeigt alle verfügbaren Templates + ↓ +7. User klickt "Erstellen" → Container spawnt +``` + +### Dateien im System + +| Datei/Ordner | Zweck | +|--------------|-------| +| `user-template-xyz/` | Template-Verzeichnis (Dockerfile + Assets) | +| `.env` → `USER_TEMPLATE_IMAGES` | Liste aller verfügbaren Images | +| `templates.json` | Metadaten (Display-Name, Beschreibung) | +| `config.py` | Lädt Templates dynamisch beim Start | +| `container_manager.py` | Spawnt Container aus Templates | + +### Template-Namen → Typ-Mapping + +Das System extrahiert automatisch den Container-Typ aus dem Image-Namen: + +```python +# Beispiele: +'user-template-01:latest' → Typ: 'template-01' +'user-template-next:latest' → Typ: 'template-next' +'user-template-python:latest' → Typ: 'template-python' +'custom-nginx:v1.0' → Typ: 'custom-nginx' +``` + +**Regel:** Image-Name ohne `user-` Prefix und ohne Tag (`:latest`) = Container-Typ + +--- + +## Anforderungen an Templates + +### Pflicht-Anforderungen + +Jedes Template **MUSS**: + +1. **Port 8080 exposen** (unprivileged, kein root) + ```dockerfile + EXPOSE 8080 + ``` + +2. **Webserver auf Port 8080 laufen lassen** + - Nginx: `listen 8080;` + - Node.js: `app.listen(8080)` + - Flask: `app.run(port=8080, host='0.0.0.0')` + +3. **Als unprivileged User laufen** (Sicherheit) + ```dockerfile + USER nginx # oder node, www-data, etc. + ``` + +4. **HTTP-Server bereitstellen** (Traefik routet HTTP-Traffic) + +### Optionale Features + +Templates **KÖNNEN**: + +- Datenbank-Container integrieren (via Docker Compose in Template) +- Umgebungsvariablen nutzen (via ENV in Dockerfile) +- Volume-Mounts für persistente Daten (via docker-compose.yml) +- Build-Args für Konfiguration (z.B. `ARG NODE_VERSION=18`) + +### Was NICHT funktioniert + +Templates **KÖNNEN NICHT**: + +- Andere Ports als 8080 nutzen (Traefik-Konfiguration) +- Root-Rechte benötigen (Security Policy) +- Direkten Zugriff auf Docker Socket (Isolation) +- Andere Container spawnen (nur eigener Container) + +--- + +## Neues Template erstellen + +### Schritt 1: Verzeichnis erstellen + +```bash +cd /path/to/spawner +mkdir user-template-myapp +cd user-template-myapp +``` + +**Namenskonvention:** `user-template-` +- ``: Eindeutiger Identifier (lowercase, keine Sonderzeichen außer `-`) +- Beispiele: `user-template-python`, `user-template-django`, `user-template-flask` + +### Schritt 2: Dockerfile erstellen + +**Basis-Vorlage (Nginx):** +```dockerfile +FROM nginx:alpine + +# Expose Port 8080 (unprivileged) +EXPOSE 8080 + +# Kopiere statische Dateien +COPY index.html /usr/share/nginx/html/ + +# Nginx auf Port 8080 konfigurieren +RUN sed -i 's/listen 80;/listen 8080;/' /etc/nginx/conf.d/default.conf + +# Run as unprivileged user +USER nginx + +CMD ["nginx", "-g", "daemon off;"] +``` + +**Erweiterte Vorlage (Node.js):** +```dockerfile +FROM node:18-alpine + +# Working Directory +WORKDIR /app + +# Install Dependencies +COPY package*.json ./ +RUN npm ci --only=production + +# Copy Application Code +COPY . . + +# Expose Port 8080 +EXPOSE 8080 + +# Run as unprivileged user +USER node + +# Start Application +CMD ["node", "server.js"] +``` + +**Wichtig:** Port 8080 ist Pflicht! + +### Schritt 3: Template-Assets hinzufügen + +**Beispiel: Nginx mit statischer index.html** +```bash +cat > index.html <<'EOF' + + + + + Mein Custom Template + + +

Willkommen zu meinem Custom Container!

+

Dieser Container läuft auf Port 8080.

+ + +EOF +``` + +**Beispiel: Node.js mit server.js** +```bash +cat > server.js <<'EOF' +const express = require('express'); +const app = express(); +const PORT = 8080; + +app.get('/', (req, res) => { + res.send('

Node.js Template

Läuft auf Port 8080

'); +}); + +app.listen(PORT, '0.0.0.0', () => { + console.log(`Server läuft auf Port ${PORT}`); +}); +EOF +``` + +### Schritt 4: `.env` aktualisieren + +Öffne die `.env` Datei im Hauptverzeichnis: +```bash +nano .env +``` + +Füge dein Template zur `USER_TEMPLATE_IMAGES` Liste hinzu: +```bash +# Vorher: +USER_TEMPLATE_IMAGES="user-template-01:latest;user-template-02:latest;user-template-next:latest" + +# Nachher: +USER_TEMPLATE_IMAGES="user-template-01:latest;user-template-02:latest;user-template-next:latest;user-template-myapp:latest" +``` + +**Wichtig:** Semikolon-getrennt, keine Leerzeichen, mit `:latest` Tag! + +### Schritt 5: `templates.json` aktualisieren + +Öffne `templates.json`: +```bash +nano templates.json +``` + +Füge Metadaten für dein Template hinzu: +```json +{ + "templates": [ + { + "type": "template-01", + "image": "user-template-01:latest", + "display_name": "Nginx Basic", + "description": "Einfacher Nginx-Server mit statischen Dateien" + }, + { + "type": "template-02", + "image": "user-template-02:latest", + "display_name": "Nginx Advanced", + "description": "Nginx mit erweiterten Features" + }, + { + "type": "template-next", + "image": "user-template-next:latest", + "display_name": "Next.js Production", + "description": "React-App mit Shadcn/UI" + }, + { + "type": "template-myapp", + "image": "user-template-myapp:latest", + "display_name": "Meine Custom App", + "description": "Mein eigenes Template mit Node.js" + } + ] +} +``` + +**Felder:** +- `type`: Muss mit extrahiertem Typ übereinstimmen (`template-myapp`) +- `image`: Vollständiger Image-Name mit Tag +- `display_name`: Name im Dashboard (beliebig) +- `description`: Kurze Beschreibung für User + +### Schritt 6: Template lokal bauen + +```bash +cd /path/to/spawner +docker build -t user-template-myapp:latest user-template-myapp/ +``` + +**Überprüfung:** +```bash +docker images | grep user-template-myapp +# Expected: user-template-myapp latest abc123def456 5 seconds ago 150MB +``` + +### Schritt 7: Template testen + +Starte einen Test-Container: +```bash +docker run -d -p 8080:8080 --name test-myapp user-template-myapp:latest +``` + +Teste im Browser: +```bash +curl http://localhost:8080 +# Expected: HTML-Output deines Templates +``` + +Cleanup: +```bash +docker stop test-myapp +docker rm test-myapp +``` + +### Schritt 8: Änderungen committen + +```bash +git add user-template-myapp/ .env templates.json +git commit -m "feat: add custom template-myapp" +git push +``` + +--- + +## Template-Beispiele + +### Beispiel 1: Python Flask Template + +**Verzeichnisstruktur:** +``` +user-template-flask/ +├── Dockerfile +├── requirements.txt +└── app.py +``` + +**Dockerfile:** +```dockerfile +FROM python:3.11-slim + +WORKDIR /app + +# Install Dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy Application +COPY app.py . + +# Expose Port 8080 +EXPOSE 8080 + +# Run as unprivileged user +RUN useradd -m appuser +USER appuser + +# Start Flask +CMD ["python", "app.py"] +``` + +**requirements.txt:** +``` +Flask==3.0.0 +gunicorn==21.2.0 +``` + +**app.py:** +```python +from flask import Flask +app = Flask(__name__) + +@app.route('/') +def home(): + return '

Flask Template

Läuft auf Port 8080

' + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=8080) +``` + +**In `.env` hinzufügen:** +```bash +USER_TEMPLATE_IMAGES="...;user-template-flask:latest" +``` + +**In `templates.json` hinzufügen:** +```json +{ + "type": "template-flask", + "image": "user-template-flask:latest", + "display_name": "Python Flask", + "description": "Flask Web-Framework für Python" +} +``` + +### Beispiel 2: Static Site Generator (Hugo) + +**Verzeichnisstruktur:** +``` +user-template-hugo/ +├── Dockerfile +├── config.toml +└── content/ + └── _index.md +``` + +**Dockerfile:** +```dockerfile +FROM klakegg/hugo:alpine AS builder + +WORKDIR /src +COPY . . +RUN hugo --minify + +FROM nginx:alpine + +# Copy built site +COPY --from=builder /src/public /usr/share/nginx/html + +# Nginx on Port 8080 +RUN sed -i 's/listen 80;/listen 8080;/' /etc/nginx/conf.d/default.conf + +EXPOSE 8080 +USER nginx + +CMD ["nginx", "-g", "daemon off;"] +``` + +**config.toml:** +```toml +baseURL = "/" +languageCode = "de-de" +title = "Hugo Template" +theme = "ananke" +``` + +### Beispiel 3: Database-Backed Template (Node.js + PostgreSQL) + +**Hinweis:** Für Multi-Container-Templates (mit DB) nutze `docker-compose.yml` **innerhalb** des Templates. + +**Verzeichnisstruktur:** +``` +user-template-node-db/ +├── Dockerfile +├── docker-compose.yml +├── package.json +└── server.js +``` + +**Dockerfile:** +```dockerfile +FROM node:18-alpine + +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only=production + +COPY . . + +EXPOSE 8080 +USER node + +CMD ["node", "server.js"] +``` + +**docker-compose.yml (wird vom Container genutzt):** +```yaml +version: '3.8' +services: + app: + build: . + ports: + - "8080:8080" + environment: + - DATABASE_URL=postgresql://user:password@db:5432/mydb + depends_on: + - db + + db: + image: postgres:15-alpine + environment: + POSTGRES_USER: user + POSTGRES_PASSWORD: password + POSTGRES_DB: mydb + volumes: + - db-data:/var/lib/postgresql/data + +volumes: + db-data: +``` + +**Wichtig:** Spawner erstellt nur den Main-Container. Für Multi-Container nutze einen Wrapper-Script oder starte via `docker-compose` im Container. + +--- + +## Deployment + +### Lokale Entwicklung + +```bash +# 1. Template erstellen +mkdir user-template-xyz +cd user-template-xyz +# ... Dockerfile erstellen ... + +# 2. .env und templates.json aktualisieren +nano ../.env +nano ../templates.json + +# 3. Template bauen +docker build -t user-template-xyz:latest . + +# 4. Spawner neu starten +cd .. +docker-compose restart spawner + +# 5. Dashboard öffnen +# Template sollte jetzt sichtbar sein +``` + +### Production Deployment (Server) + +**Schritt 1: Code committen** +```bash +git add user-template-xyz/ .env templates.json +git commit -m "feat: add template-xyz" +git push +``` + +**Schritt 2: Auf Server deployen** +```bash +# SSH zum Server +ssh user@server + +# Navigiere zum Spawner-Verzeichnis +cd /volume1/docker/spawner + +# Pull neueste Änderungen +git pull + +# Baue neues Template +docker build -t user-template-xyz:latest user-template-xyz/ + +# Kopiere aktualisierte Config-Dateien in Container +docker cp .env spawner:/app/.env +docker cp templates.json spawner:/app/templates.json + +# Restart Spawner (lädt neue Konfiguration) +docker-compose restart spawner +``` + +**Schritt 3: Verifikation** +```bash +# Überprüfe ob Template geladen wurde +docker-compose logs spawner | grep "template-xyz" + +# Teste via Debug-API +curl -H "X-Debug-Token: " \ + "http://localhost:5000/api/admin/debug?action=info" + +# Dashboard öffnen und neues Template prüfen +``` + +### Automatisiertes Deployment (install.sh) + +Das `install.sh` Script baut **automatisch alle** `user-template-*` Verzeichnisse: + +```bash +# Auf Server: +cd /volume1/docker/spawner +git pull +bash install.sh +``` + +**Was install.sh macht:** +1. Findet alle `user-template-*` Verzeichnisse +2. Baut Docker Images für jedes Template +3. Startet Services neu +4. Templates erscheinen automatisch im Dashboard + +**Vorteil:** Keine manuellen Docker-Builds nötig! + +--- + +## Troubleshooting + +### Problem: Template erscheint nicht im Dashboard + +**Symptom:** Neues Template ist nicht in der Container-Liste sichtbar + +**Lösung:** +```bash +# 1. Überprüfe .env im Container +docker exec spawner cat /app/.env | grep USER_TEMPLATE_IMAGES + +# 2. Überprüfe templates.json im Container +docker exec spawner cat /app/templates.json + +# 3. Überprüfe Backend-Logs +docker-compose logs spawner | grep -i template + +# 4. Falls nicht aktualisiert, kopiere Dateien manuell: +docker cp .env spawner:/app/.env +docker cp templates.json spawner:/app/templates.json +docker-compose restart spawner +``` + +### Problem: Container spawnt nicht / Fehler beim Start + +**Symptom:** Klick auf "Erstellen" → Fehler "Container konnte nicht erstellt werden" + +**Lösung:** +```bash +# 1. Überprüfe ob Image existiert +docker images | grep user-template-xyz + +# 2. Falls nicht vorhanden, baue neu: +docker build -t user-template-xyz:latest user-template-xyz/ + +# 3. Teste Image manuell +docker run -d -p 8080:8080 --name test-xyz user-template-xyz:latest + +# 4. Überprüfe Logs +docker logs test-xyz + +# 5. Cleanup +docker stop test-xyz && docker rm test-xyz + +# 6. Überprüfe Spawner-Logs +docker-compose logs spawner | tail -50 +``` + +### Problem: Port 8080 nicht erreichbar + +**Symptom:** Container läuft, aber `curl http://localhost:8080` gibt Timeout + +**Lösung:** +```bash +# 1. Überprüfe ob Container wirklich auf 8080 hört +docker exec netstat -tlnp | grep 8080 + +# 2. Überprüfe Dockerfile +cat user-template-xyz/Dockerfile | grep EXPOSE +# Expected: EXPOSE 8080 + +# 3. Überprüfe Webserver-Konfiguration +# Nginx: listen 8080; +# Node.js: app.listen(8080, '0.0.0.0') +# Flask: app.run(port=8080, host='0.0.0.0') + +# 4. Rebuilde Template mit korrektem Port +docker build -t user-template-xyz:latest user-template-xyz/ +``` + +### Problem: Traefik routet nicht zum Container + +**Symptom:** URL öffnet, aber zeigt 404 oder Timeout + +**Lösung:** +```bash +# 1. Überprüfe Container-Labels +docker inspect user--- | grep -A10 traefik + +# 2. Überprüfe Traefik Dashboard +# http://:8080 → HTTP Routers + +# 3. Überprüfe StripPrefix Middleware +curl http://localhost:8080/api/http/middlewares | jq . | grep user + +# 4. Überprüfe Traefik Logs +docker-compose logs traefik | grep user- + +# 5. Starte Traefik neu +docker-compose restart traefik +``` + +### Problem: Datei-Änderungen werden nicht übernommen + +**Symptom:** Dockerfile geändert, aber Container nutzt alte Version + +**Lösung:** +```bash +# 1. IMMER --no-cache beim Rebuild verwenden +docker build --no-cache -t user-template-xyz:latest user-template-xyz/ + +# 2. Alte Container entfernen +docker ps -a | grep user-template-xyz | awk '{print $1}' | xargs docker rm -f + +# 3. Neue Container spawnen (via Dashboard oder API) + +# 4. Überprüfe Image-Erstellungsdatum +docker images | grep user-template-xyz +# Should show recent timestamp +``` + +--- + +## Best Practices + +### Sicherheit + +1. **Unprivileged Users verwenden** + ```dockerfile + # NICHT als root laufen + USER nginx # oder node, www-data, appuser, etc. + ``` + +2. **Minimale Base Images** + ```dockerfile + # Bevorzuge alpine-Varianten + FROM node:18-alpine # statt node:18 + FROM python:3.11-slim # statt python:3.11 + ``` + +3. **Keine Secrets im Image** + ```dockerfile + # ❌ FALSCH + ENV API_KEY=secret123 + + # ✅ RICHTIG - Via Runtime-Env + # Secrets werden vom Spawner injiziert + ``` + +### Performance + +1. **Multi-Stage Builds nutzen** + ```dockerfile + # Build Stage + FROM node:18-alpine AS builder + WORKDIR /app + COPY package*.json ./ + RUN npm ci + COPY . . + RUN npm run build + + # Runtime Stage + FROM nginx:alpine + COPY --from=builder /app/dist /usr/share/nginx/html + ``` + +2. **Layer Caching optimieren** + ```dockerfile + # Dependencies zuerst (ändern sich selten) + COPY package*.json ./ + RUN npm ci + + # Code danach (ändert sich oft) + COPY . . + ``` + +3. **Image-Größe minimieren** + ```dockerfile + # Cleanup in einer RUN-Anweisung + RUN apt-get update && \ + apt-get install -y pkg1 pkg2 && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + ``` + +### Wartbarkeit + +1. **Versionierung nutzen** + ```bash + # Statt :latest auch spezifische Versionen taggen + docker build -t user-template-xyz:v1.0.0 . + docker tag user-template-xyz:v1.0.0 user-template-xyz:latest + ``` + +2. **README.md pro Template** + ```markdown + # Template XYZ + + ## Was macht dieses Template? + - Beschreibung + - Features + - Use Cases + + ## Konfiguration + - Umgebungsvariablen + - Volumes + - Ports + + ## Entwicklung + - Lokales Setup + - Tests + - Debugging + ``` + +3. **Dokumentierte Umgebungsvariablen** + ```dockerfile + # ENV-Variablen mit Defaults + ENV NODE_ENV=production \ + PORT=8080 \ + LOG_LEVEL=info + ``` + +### Testing + +1. **Health Checks definieren** + ```dockerfile + HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:8080/health || exit 1 + ``` + +2. **Test-Script erstellen** + ```bash + #!/bin/bash + # test-template.sh + + IMAGE="user-template-xyz:latest" + CONTAINER="test-xyz" + + # Build + docker build -t $IMAGE . + + # Run + docker run -d -p 8080:8080 --name $CONTAINER $IMAGE + + # Wait + sleep 5 + + # Test + curl -f http://localhost:8080 || exit 1 + + # Cleanup + docker stop $CONTAINER && docker rm $CONTAINER + + echo "✅ Template funktioniert!" + ``` + +3. **Automatisierte Tests (optional)** + ```bash + # CI/CD Pipeline (GitHub Actions, GitLab CI) + # .github/workflows/test-templates.yml + + name: Test Templates + on: [push] + jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Test Template XYZ + run: bash user-template-xyz/test-template.sh + ``` + +--- + +## Zusammenfassung + +### Schnell-Anleitung + +```bash +# 1. Verzeichnis erstellen +mkdir user-template-myapp + +# 2. Dockerfile erstellen (Port 8080, unprivileged user) +nano user-template-myapp/Dockerfile + +# 3. Assets hinzufügen +cp index.html user-template-myapp/ + +# 4. .env aktualisieren +# USER_TEMPLATE_IMAGES="...;user-template-myapp:latest" + +# 5. templates.json aktualisieren +# { "type": "template-myapp", "image": "...", ... } + +# 6. Bauen & Testen +docker build -t user-template-myapp:latest user-template-myapp/ +docker run -d -p 8080:8080 --name test user-template-myapp:latest +curl http://localhost:8080 +docker stop test && docker rm test + +# 7. Committen +git add user-template-myapp/ .env templates.json +git commit -m "feat: add template-myapp" +git push + +# 8. Auf Server deployen +ssh user@server +cd /volume1/docker/spawner +git pull +docker build -t user-template-myapp:latest user-template-myapp/ +docker cp .env spawner:/app/.env +docker cp templates.json spawner:/app/templates.json +docker-compose restart spawner +``` + +### Checkliste + +- [ ] Template-Verzeichnis `user-template-/` erstellt +- [ ] Dockerfile mit Port 8080 und unprivileged user +- [ ] Template lokal gebaut und getestet +- [ ] `.env` → `USER_TEMPLATE_IMAGES` aktualisiert +- [ ] `templates.json` mit Metadaten erweitert +- [ ] Änderungen committed und gepusht +- [ ] Auf Server deployed (git pull + docker cp + restart) +- [ ] Dashboard überprüft (Template sichtbar?) +- [ ] Container erfolgreich erstellt und erreichbar + +--- + +## Support & Weitere Informationen + +- **Hauptdokumentation**: `docs/install/DEPLOYMENT_GUIDE.md` +- **Architektur**: `CLAUDE.md` +- **Troubleshooting**: `docs/install/DEPLOYMENT_GUIDE.md#troubleshooting` +- **API-Dokumentation**: `docs/api/` (falls vorhanden) + +Bei Fragen: Issue auf GitHub/Gitea erstellen oder Admin kontaktieren.