Deployment
Diese Seite erklaert, wie ein Theme-Build aussieht (Dev vs. Production), welche Deployment-Strategien moeglich sind und wie das Staging-to-Live-Deployment-System des Update Managers funktioniert. nginx-Beispielkonfiguration und typische Fallstricke inklusive.
Build-Kommandos
Jedes Theme (_theme/vue-base/, _theme/projekt01/, ...) hat ein eigenes package.json mit Standard-Nuxt-Scripts.
Einmaliges Setup:
cd _theme/vue-base
npm installDie vier npm-Scripts:
| Script | Zweck |
|---|---|
npm run dev | Dev-Server auf http://localhost:3000 mit Hot-Reload |
npm run build | SSR-Production-Build nach .output/ |
npm run generate | Statischer Build (SSG) nach .output/public/ |
npm run preview | Preview des Production-Builds |
Jedes Theme-Verzeichnis hat eigene node_modules/ und eigenen Build-Output — kein globales npm install im Repo-Root.
build vs. generate — SSR vs. SSG
| Kommando | Output | Wann? |
|---|---|---|
npm run build | .output/ (Node-Server + Client-Bundle) | SSR-Deployment, braucht Node.js-Laufzeit beim Hosting |
npm run generate | .output/public/ (nur statische HTML + Assets) | SSG-Deployment, nur statischer Webserver noetig (nginx, Apache, CDN) |
Im CMS kommt meist npm run build zum Einsatz — das Nuxt-Theme laeuft als Node-Prozess und holt Content dynamisch ueber /api/pages. Bei reiner statischer Auslieferung waere generate moeglich, aber der Pagebuilder-Content und Session-abhaengige Widgets wuerden dann beim Build eingefroren.
Output-Struktur nach npm run build
_theme/vue-base/.output/
├── nitro.json # Nitro-Server-Meta
├── public/ # Statische Assets (client-side)
│ ├── _nuxt/ # Gebundelte JS + CSS
│ │ ├── entry.*.css
│ │ └── *.woff2
│ ├── backend/ # Admin-CSS/JS (Copy aus public/)
│ ├── favicon.ico
│ └── robots.txt
└── server/ # Nitro-Server (SSR)
├── index.mjs # Entry-Point
└── chunks/.output/server/index.mjs ist der Einstieg fuer Production: node .output/server/index.mjs startet einen Listen-Port (default 3000).
Plesk-Deploy-Beispiel (deploy.sh)
Das Repo liefert unter _theme/vue-base/deploy.sh ein reales Plesk-Deploy-Skript mit:
#!/bin/bash
export PATH=/opt/plesk/node/25/bin:$PATH
cd /pfad/zum/theme/_theme/vue-base
echo "Building Nuxt..."
npm run build >> build.log 2>&1
# Font + CSS-Dateinamen in JSON speichern (fuer Admin-CSS-Isolation)
ls .output/public/_nuxt/*.woff2 .output/public/_nuxt/entry.*.css 2>/dev/null | \
xargs -I{} basename {} | \
python3 -c "..."
echo "Restarting App..."
mkdir -p tmp
touch tmp/restart.txt # Plesk-Passenger-Restart-Trigger
echo "Done!"Die Besonderheit: fonts-manifest.json wird nach dem Build generiert — enthaelt die Hash-Dateinamen aus _nuxt/ fuer das Admin-CSS-Isolations-Plugin (admin-css.client.ts). Ohne dieses Manifest laedt das Admin-Layout keine Fonts.
Nuxt-Server hinter nginx
Fuer Production wird das Nuxt-Theme typisch hinter nginx mit Reverse-Proxy zum Node-Prozess betrieben.
Beispiel /etc/nginx/sites-available/example.com
# SSL-Redirect
server {
listen 80;
server_name www.example.com example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name www.example.com example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Nuxt-Frontend (Node-Prozess auf Port 3000)
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# PHP-Backend fuer /api/* und /admin/* (inkl. Pfade ohne Trailing-Slash)
location ~ ^/(api|admin)(/|$) {
proxy_pass http://127.0.0.1:8080; # PHP-Apache/FPM
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Upload-Ordner direkt ausliefern (Performance)
# Der echte Upload-Pfad haengt von der Installation ab — typisch /z_uploads/
# oder ein eigener Upload-Root. Die URLs kommen aus files::loadURL() serverseitig.
location ~ ^/z_uploads/ {
root /pfad/zum/cms;
expires 30d;
add_header Cache-Control "public, immutable";
}
}X-Forwarded-For muss durchgereicht werden
Der PHP-Backend nutzt $_SERVER['HTTP_X_FORWARDED_FOR'] fuer IP-basiertes Rate-Limiting, Session-Binding und Audit-Logging. Ohne proxy_set_header X-Forwarded-For sehen alle Backend-Operationen nur die nginx-lokale IP (127.0.0.1) — Rate-Limits wirken dann gegen den Proxy, nicht gegen echte Besucher.
Node-Prozess beim Boot starten
Mit systemd (Debian/Ubuntu):
# /etc/systemd/system/newmeta-frontend.service
[Unit]
Description=newmeta Nuxt Frontend
After=network.target
[Service]
Type=simple
User=www-data
WorkingDirectory=/pfad/zum/cms/_theme/vue-base
ExecStart=/usr/bin/node .output/server/index.mjs
Environment=NUXT_API_KEY=...
Environment=NUXT_PUBLIC_API_BASE=https://www.example.com/api
Environment=NUXT_PUBLIC_SITE_URL=https://www.example.com
Environment=NUXT_PUBLIC_THEME=vue-base
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.targetAktivieren:
systemctl daemon-reload # einmal nach Anlegen/Aendern der Unit
systemctl enable newmeta-frontend
systemctl start newmeta-frontend
systemctl status newmeta-frontendSpaeter bei Aenderungen an der .service-Datei nochmal systemctl daemon-reload gefolgt von systemctl restart newmeta-frontend — ohne Daemon-Reload liest systemd die alte Config.
Mit Plesk: keine systemd-Unit noetig — Plesk-Passenger uebernimmt den Node-Prozess und reagiert auf tmp/restart.txt als Reload-Trigger.
Cache-Rebuild als Deploy-Step
Nach jedem Theme-Build muss zusaetzlich der PHP-CSS-Cache neu generiert werden, damit neue Design-Tokens, Custom-CSS und plugin-registry.js wirksam werden. Primaerer Weg: Admin → /admin/cache → "Cache leeren" manuell klicken. Hinter dem Button steht apcu_clear_cache() im Script cache_settings/script/cache_clear.php plus Cache-Rebuild via cache_compress.php.
Fuer automatisierte Deploy-Pipelines laesst sich der entsprechende Backend-Endpoint direkt via API-Key ansprechen — der exakte Pfad ist installationsabhaengig und im API-Key-Panel abrufbar.
Was genau passiert, haengt davon ab, was geaendert wurde:
- Nur Theme-Code (Vue, Styles):
npm run build+systemctl restart newmeta-frontendreicht - Neues Widget oder Custom-Override: zusaetzlich Cache-Rebuild, damit
plugin-registry.jsneu generiert wird - Design-Tokens-Aenderung via
/admin/design: Core triggert Rebuild automatisch nach Save
Details: Nuxt-Aliases › Wann wird plugin-registry.js neu gebaut?.
Staging-to-Live via Update Manager
Das CMS bringt einen eigenen Staging-to-Live-Deployment-Mechanismus mit — unter /admin/update (Bereich "Deployments") lassen sich Ziel-Umgebungen konfigurieren und per Klick ausrollen.
Konzept
[Staging-Server] ──────→ [Production-Server]
Deploy
1. ZIP-Archiv des Codes erstellen (exclude .git, node_modules, .nuxt, .output, .claude)
2. MySQL-Dump der Staging-DB erstellen
3. Dateien ins Ziel-Verzeichnis entpacken (mit Backup von config.local.php)
4. Tabellen im Ziel droppen + aus Dump importieren
5. URL-Replace: staging.example.com → www.example.com
(serialized-aware: unserialize → recursive replace → serialize)
6. Webhook `deployment.completed` dispatchenKonfiguration
Pro Ziel-Umgebung ein Eintrag in deployment_targets mit:
- DB-Credentials (AES-256-CBC verschluesselt)
- Ziel-Verzeichnis
- Search/Replace-URL-Paar
- Aktiv-Flag
Details in der Admin-UI /admin/update → "Deployments".
7-Schritte-Ablauf
- ZIP erstellen — aus dem aktuellen Code
- DB-Dump —
mysqldumpodermariadb-dump(Auto-Detection) - Files kopieren —
config.local.phpdes Zielsystems bleibt erhalten - Ziel-DB leeren — alle Tabellen droppen
- DB importieren — Dump einspielen
- URL-Replace + Ident — serialized-aware, JSON-escaped, mit
updateWebsiteIdent() - Finalize — Status-Update, Webhook-Dispatch, Log-Eintrag
Ausschluesse
Tabellen wie Sessions, Logs, Queue, Cache werden in deployment_targets/config/deploy_exclude_tables.json vom Dump ausgeschlossen — ausser die Ziel-DB ist leer (Erst-Deploy), dann werden alle Tabellen eingespielt.
Nicht im Scope
Das Staging-to-Live-System kuemmert sich um PHP-Code + DB + Media. Das Nuxt-Theme muss separat deployed werden (npm run build auf dem Ziel-Server + App-Restart). Typischer Ablauf:
1. Auf Staging: Pagebuilder-Seiten publishen, testen
2. In Admin: /admin/update → Deployments → Ziel waehlen → "Deploy"
→ PHP-Code + DB + Media landen auf Production
3. Auf Production-Server: cd _theme/vue-base && ./deploy.sh
→ Nuxt-Build + App-RestartHaeufige Fehler
X-Forwarded-For-Header fehlt im nginx-Proxy
Backend sieht nur 127.0.0.1 als IP → Rate-Limiter greift global, Session-Binding bricht, IP-Audit-Log ist nutzlos. Immer proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for im nginx-Config setzen.
Nuxt-Build mit falscher NUXT_PUBLIC_API_BASE
Die Env-Variable wird zum Build-Zeitpunkt ins Client-Bundle gebacken. Production-Build mit Staging-URL fuehrt zu Mixed-Content-Fehlern oder falschen API-Calls. .env vor npm run build pruefen, oder Build-Env explizit setzen: NUXT_PUBLIC_API_BASE=https://www.example.com/api npm run build.
App-Restart nach Build vergessen
npm run build aktualisiert .output/, aber der laufende Node-Prozess haelt weiter den alten Code. systemctl restart newmeta-frontend (oder touch tmp/restart.txt bei Plesk) ist Pflicht nach jedem Build.
Cache-Rebuild nach Widget-Aenderung vergessen
Neue widgets/*/template/index.vue-Datei → plugin-registry.js braucht Neugenerierung. Ohne Cache-Rebuild zeigt der Pagebuilder-Picker das Widget, aber das Rendering wirft Unknown widget type — weil die Registry das widget_template nicht kennt.
Deploy waehrend des Staging-Tests
Das Staging-to-Live loescht alle Tabellen im Ziel und ersetzt durch Staging-Dump. Wenn Production-User parallel Daten eingeben, gehen die verloren. Deploy immer in Wartungsfenstern oder nach explizitem Freeze auf Production.
deploy.sh mit falschen Pfaden
Das reale deploy.sh im Repo hat hart-codierte Pfade (/var/www/vhosts/...). Fuer andere Hosts unbedingt anpassen — sonst schreibt der Build in Fremd-Verzeichnisse oder wirft cd-Fehler.
Siehe auch
- Theme-Struktur —
package.json+ Scripts,.output/-Layout - Environment-Variablen — Production-Env-Setzen vor Build
- Design-Tokens › Cache-Integration — wann Cache-Rebuild noetig ist
- Nuxt-Aliases —
plugin-registry.jsund Auto-Generation _theme/vue-base/deploy.sh— reales Plesk-Deploy-Script im Repo_public/extensions/core/backend/updatemanager/— Staging-to-Live-System