cipi deploy

Cipi verwendet Bereitsteller für alle Einsätze. Jede Bereitstellung ist atomar: eine neue Version Das Verzeichnis wird vor dem vollständig vorbereitet current Der symbolische Link wird vertauscht, der Datenverkehr also nie unterbrochen.

Seitdem v4.5.4 Cipi Pakete Bereitsteller 8, was erfordert PHP ≥ 8.3. cipi deploy und cipi deploy --rollback Abbruch mit einer klaren Upgrade-Meldung vor Aufrufen von Deployer, wenn eine App noch angeheftet ist auf eine ältere PHP-Version – wechseln Sie mit cipi app edit <app> --php=8.3 (oder höher) zuerst.

Pipeline bereitstellen

Deployer und Composer laufen mit dem Die konfigurierte PHP-Version der App (z.B. /usr/bin/php8.5), nicht der Systemstandard. Dies gilt für cipi deploy, cipi deploy --rollback, Crontab-Bereitstellungstrigger, cipi sync import setzt ein, und die deploy / composerAliase in den App-Benutzern .bashrc.

  1. Warteschlangenarbeiter stoppen (cipi worker stop)
  2. Klonen Sie das Repo in releases/N/
  3. Lauf composer install --no-dev (mit PHP der App)
  4. Link shared/.env und shared/storage/
  5. Lauf artisan migrate --force
  6. Lauf artisan optimize
  7. Lauf artisan storage:link
  8. Tauschen current Symlink atomar
  9. Starten Sie die Warteschlangenarbeiter neu
  10. Alte Veröffentlichungen bereinigen (die letzten 5 behalten)
bash
$ cipi deploy myapp              # deploy latest commit
$ cipi deploy myapp --rollback   # instant rollback to previous release
$ cipi deploy myapp --releases   # list all releases with timestamps
$ cipi deploy myapp --key        # show the SSH deploy key
$ cipi deploy myapp --webhook    # show webhook URL and token
$ cipi deploy myapp --unlock     # remove a stuck deploy lock
$ cipi deploy myapp --snapshot   # v5.0+ opt-in DB dump before deploy
$ cipi deploy myapp --snapshot-required  # fail if snapshot cannot be taken
$ cipi deploy myapp --trust-host=git.mycompany.com       # trust a custom Git server fingerprint
$ cipi deploy myapp --trust-host=git.mycompany.com:2222  # trust on non-standard port (also writes ~/.ssh/config)

Seitdem v5.0, Opt-in DB-Snapshots vorab bereitstellen sind erhältlich über --snapshot / --snapshot-required oder eine permanente App-Einstellung – siehe Stellen Sie DB-Snapshots vorab bereit. Octane Apps verwenden die laravel-octane.php Deployer-Vorlage (Octane bei der Bereitstellung neu laden/neu starten); Knoten aktivieren baut mit cipi app edit <app> --node-build='…'.

Wenn eine Bereitstellung unterbrochen wird (z. B. durch einen Netzwerkfehler), hinterlässt der Deployer möglicherweise eine Sperrdatei. Benutzen cipi deploy myapp --unlock um es vor der erneuten Bereitstellung zu entfernen.

Stellen Sie DB-Snapshots vorab bereit

Seitdem v5.0, cipi deploy <app> kann die Datenbank sichern vor Die Release-Pipeline wird ausgeführt. Schnappschüsse landen darunter /var/log/cipi/backups/ – derselbe Pfad, den auch verwendet wird cipi db backup.

bash
$ cipi deploy shop --snapshot            # dump first; warn and continue on failure
$ cipi deploy shop --snapshot-required   # dump first; abort deploy if snapshot fails

Was passiert

  1. Bevor Deployer startet, erstellt Cipi einen DB-Dump /var/log/cipi/backups/.
  2. Mit --snapshot-required, ein fehlgeschlagener Dump (oder eine fehlende DB-Engine) Blöcke die Bereitstellung.
  3. Mit --snapshot Allein bei einem fehlgeschlagenen Dump wird eine Warnung und die Bereitstellung ausgegeben geht weiter.
--snapshotOpt-in-Dump vor der Bereitstellung; warnen und fortfahren, wenn der Schnappschuss nicht erstellt werden kann.
--snapshot-requiredGleicher Dump, aber die Bereitstellung schlägt fehl, wenn der Snapshot (oder die Engine) nicht verfügbar ist.

Bei jeder Bereitstellung aktivieren

Aktivieren Sie es dauerhaft für eine App, damit bei jeder Bereitstellung (CLI, webhook oder Pipeline) zuerst ein Snapshot erstellt wird:

bash
$ cipi app edit shop --predeploy-snapshot
cipi deploy --rollback stellt das Vorherige wieder her Code Nur Freigabe. Das tut es nicht die Datenbank wiederherstellen. Wenn Sie den Pre-Deploy-Dump zurück benötigen, verwenden Sie cipi db restore.

Für Pipeline-Workflows, die auch archivieren shared/ bis S3 vor der Veröffentlichung, siehe Sichere Bereitstellung – Sicherung vor der Veröffentlichung.

cipi app deploy-config

Seitdem v5.0.3, verwalten Sie dauerhafte Deployer-Rezeptoptionen, die in gespeichert sind apps.json und durch Regenerieren angewendet deploy.php aus der Vorlage – eine sichere Alternative zum Bearbeiten von Freiform-PHP.

bash
$ cipi app deploy-config myapp
$ cipi app deploy-config myapp --keep-releases=5
$ cipi app deploy-config myapp --migrate --optimize --storage-link
$ cipi app deploy-config myapp --no-migrate --no-optimize
$ cipi app deploy-config myapp --queue-restart --horizon-terminate
$ cipi app deploy-config myapp --extra-artisan=view:clear,event:cache
$ cipi app deploy-config myapp --node-build='npm ci && npm run build'
$ cipi app deploy-config myapp --predeploy-snapshot

RUHE: GET|PUT /api/apps/{name}/deploy-config (Fähigkeit apps-deploy-config, API 1.14+ / Cipi 5.0.3+). MCP: AppDeployConfigShow, AppDeployConfigUpdate.

auth.json

Verwalten Sie die auth.json Datei für eine App. Diese Datei befindet sich unter /home/<app>/shared/auth.json und wird automatisch mit jeder Veröffentlichung von verknüpft Deployer – genau so .env. Verwenden Sie es, um strukturierte Anmeldedaten zu speichern (z. B. API Schlüssel, Feature-Flags oder jede JSON-Nutzlast), die Ihre Laravel-App zur Laufzeit lesen kann.

bash
$ cipi auth create myapp   # create auth.json with initial { "users": [] } structure
$ cipi auth edit myapp     # open in $EDITOR (fallback: nano), validate JSON on close
$ cipi auth show myapp     # print contents formatted with jq
$ cipi auth delete myapp   # delete file (asks for confirmation)

# Non-interactive (v5.0.3+) — API / scripts / GUI
$ cipi auth create myapp --force
$ cipi auth edit myapp --file=/tmp/auth.json
$ cipi auth show myapp --json
$ cipi auth delete myapp --force

RUHE: GET|POST|PUT|DELETE /api/apps/{name}/auth (Fähigkeit apps-auth, API 1.14+) – Composer/strukturiertes JSON, verschieden von HTTP Basic Auth. MCP: AppAuthJsonShow, AppAuthJsonCreate, AppAuthJsonUpdate, AppAuthJsonDelete.

Befehlsdetails

Befehl Beschreibung
cipi auth create <app> Erstellt shared/auth.json mit der Ausgangsstruktur {"users":[]}, setzt Berechtigungen auf 640 (Inhaber app:app) und fügt hinzu auth.json zu shared_files in der Deployer-Konfiguration der App, sodass es bei jedem symbolisch verknüpft ist bereitstellen.
cipi auth edit <app> Öffnet shared/auth.json in $EDITOR (fällt zurück auf nano). Nachdem der Editor geschlossen wurde, wird JSON mit validiert jq und warnt, wenn die Datei fehlerhaft ist.
cipi auth show <app> Druckt den Inhalt von shared/auth.json formatiert mit jq.
cipi auth delete <app> Bittet um Bestätigung und löscht dann shared/auth.json und entfernt die auth.json Eintrag von shared_files im Deployer der App config.

Deployer-Integration

cipi auth create automatisch angehängt auth.json zum shared_files Liste in /home/<app>/.deployer/deploy.php, und cipi auth delete entfernt es. Dies bedeutet, dass die Datei genauso behandelt wird .env: Es bleibt über alle Releases hinweg bestehen und wird nie durch eine Bereitstellung überschrieben.

Jeder cipi auth Der Vorgang wird über protokolliert log_action für Überprüfbarkeit. Die AUTH Abschnitt ist auch in der Ausgabe von aufgeführt cipi help.

Git-Anbieter

Cipi ist einsatzbereit GitHub und GitLab aber es unterstützt alle anderer Git-Anbieter, der SSH-Bereitstellungsschlüssel unterstützt – keine Anbieterbindung.

Für self-hosted oder benutzerdefinierte Git-Server müssen Sie zuvor dem Host-Fingerabdruck des Servers vertrauen Der Bereitsteller kann über SSH klonen. Benutzen Sie die --trust-host Flag zum Hinzufügen des Fingerabdrucks zum App-Benutzer ~/.ssh/known_hosts automatisch:

bash
# show the deploy key and add it to your Git provider
$ cipi deploy myapp --key

# trust a custom Git server fingerprint (standard port)
$ cipi deploy myapp --trust-host=git.mycompany.com

# trust a custom Git server on a non-standard port
# (also writes ~/.ssh/config automatically)
$ cipi deploy myapp --trust-host=git.mycompany.com:2222
Wenn ein nicht standardmäßiger Port angegeben wird, schreibt Cipi auch den Host / Port Zugang zum App-Benutzer ~/.ssh/config also dass der Deployer den Server ohne zusätzliche Konfiguration erreichen kann.

Git-Auto-Setup

Wenn Sie a speichern GitHub oder GitLab Persönliches Zugriffstoken, Cipi fügt bei jeder Ausführung automatisch den SSH-Bereitstellungsschlüssel hinzu und erstellt webhook im Repositorycipi app create. Keine manuellen Schritte erforderlich.

Speichern Sie einen Token

bash
# GitHub (fine-grained or classic PAT)
$ cipi git github-token ghp_xxxxxxxxxxxxxxxxxxxx

# GitLab (gitlab.com)
$ cipi git gitlab-token glpat-xxxxxxxxxxxxxxxxxxxx

# GitLab (self-hosted — set the URL before or after the token)
$ cipi git gitlab-url https://gitlab.example.com
$ cipi git gitlab-token glpat-xxxxxxxxxxxxxxxxxxxx

GitHub Token-Berechtigungen

Feinkörnige Token (empfohlen) erforderlich Verwaltung und Webhooks eingestellt auf Lesen und schreiben auf den Zielrepositorys. Klassische Token brauche das repo Umfang.

GitLab Token-Berechtigungen

Die api Der Umfang ist das erforderliche Minimum – GitLab bietet keinen detaillierteren Umfang Dies deckt sowohl Bereitstellungsschlüssel als auch Webhooks ab.

Automatischer Lebenszyklus

Veranstaltung Was Cipi automatisch macht
app create Fügt den Bereitstellungsschlüssel hinzu und erstellt webhook im Repository über API. Die Zusammenfassung zeigt „automatisch konfiguriert ✓“ statt manueller Anleitung.
app edit --repository=... Entfernt den Bereitstellungsschlüssel + webhook aus dem alten Repository und fügt sie dann dem neuen hinzu.
app delete Entfernt den Bereitstellungsschlüssel + webhook aus dem Repository, bevor die App gelöscht wird.

cipi git Befehle

Befehl Beschreibung
cipi git status Zeigen Sie den Verbindungsstatus des Anbieters und die Integrationsdetails pro App an (Bereitstellungsschlüssel-ID, webhook ID)
cipi git github-token <token> Speichern Sie ein GitHub persönliches Zugriffstoken
cipi git gitlab-token <token> Speichern Sie ein GitLab persönliches Zugriffstoken
cipi git gitlab-url <url> Legen Sie die Basis-URL für eine self-hosted GitLab-Instanz fest
cipi git remove-github Entfernen Sie das gespeicherte GitHub-Token
cipi git remove-gitlab Entfernen Sie das gespeicherte GitLab-Token und die URL

Manuelle Einrichtung (Fallback)

Die automatische Einrichtung wird übersprungen, wenn kein Token konfiguriert ist, wenn der API-Aufruf fehlschlägt (falsche Berechtigungen, Repository nicht gefunden, Ratenbegrenzung) oder wenn das Repository bei einem anderen Anbieter als gehostet wird GitHub oder GitLab (z. B. Gitea, Forgejo, Bitbucket). In all diesen Fällen greift Cipi auf zurück Der manuelle Workflow erfolgt und die App-Erstellung verläuft normal.

So konfigurieren Sie den Bereitstellungsschlüssel und webhook manuell:

bash
# print the SSH deploy key to add to your Git provider
$ cipi deploy myapp --key

# print the webhook URL and token
$ cipi deploy myapp --webhook

# if using a custom Git server, trust the host fingerprint first
$ cipi deploy myapp --trust-host=git.mycompany.com

Fügen Sie sie dann in den Repository-Einstellungen Ihres Anbieters hinzu:

  • Schlüssel bereitstellen — GitHub: Einstellungen → Schlüssel bereitstellen → Bereitstellungsschlüssel hinzufügen; GitLab: Einstellungen → Repository → Schlüssel bereitstellen
  • Webhook — GitHub: Einstellungen → Webhooks → webhook hinzufügen; GitLab: Einstellungen → Webhooks → Neues webhook hinzufügen. Legen Sie die Payload-URL fest und Geheimnis der von gezeigten Werte cipi deploy myapp --webhook
Wenn Sie ein Anbieter-Token entfernen, nachdem Apps mit der automatischen Einrichtung erstellt wurden, wird Cipi dies nicht tun Sie können Bereitstellungsschlüssel und Webhooks bereinigen, wenn Sie diese Apps löschen oder bearbeiten. Eine Warnung ist angezeigt und Sie müssen sie manuell aus den Repository-Einstellungen des Anbieters entfernen.

Anpassen des Bereitstellungsskripts

Die Bereitstellungskonfiguration für jede App wird gespeichert unter:

/home/meineapp/.deployer/deploy.php

Diese Datei wird von Cipi automatisch generiert app create und automatisch aktualisiert, wenn Sie Ändern Sie die PHP-Version oder stellen Sie den Zweig bereit über cipi app edit. Sie können es bearbeiten, um es anzupassen die Bereitstellungspipeline, aber Sie sollten die Auswirkungen verstehen, bevor Sie dies tun.

Standardbereitstellungspipeline

Die automatisch generierte deploy.php führt diese Aufgaben der Reihe nach aus:

php
deploy:prepare          // create releases/N/ directory
deploy:vendors          // composer install --no-dev
deploy:shared           // link shared/.env and shared/storage/
artisan:migrate         // php artisan migrate --force
artisan:optimize        // php artisan optimize
artisan:storage:link    // php artisan storage:link
deploy:symlink          // swap current → releases/N/ atomically
cipi:restart-workers    // supervisorctl restart myapp-*
deploy:cleanup          // keep last 5 releases, delete older

Benutzerdefinierte Aufgaben hinzufügen

Sie können Aufgaben vor oder nach jedem Schritt hinzufügen. Für ein vollständiges Frontend-Build-Beispiel (npm install && npm run build), siehe Aufbau von Frontend-Assets unten. Weitere häufige Beispiele:

php
// Run artisan db:seed after migrations
after('artisan:migrate', 'artisan:db:seed');

// Clear view cache after symlink swap
after('deploy:symlink', 'artisan:view:clear');

// Custom task — send a Slack notification
task('notify:slack', function () {
    run('curl -X POST https://hooks.slack.com/... -d \'{"text":"Deployed!"}\'');
});
after('deploy:symlink', 'notify:slack');

Erstellen von Frontend-Assets (npm / Vite)

Es gibt keine dedizierte cipi CLI Flag für Frontend-Builds (z. B. npm install && npm run build). Anpassen deploy.php ist der unterstützte und erwartete Ansatz – Definiere a Bereitsteller task() und haken Sie es ein after() oder before(). Das tust du nicht Dies muss vermieden werden; Die Erweiterung der Pipeline ist genau das, wofür die Datei gedacht ist.

Cipi wird installiert Node.js und npm auf dem Server während der Einrichtung. Stellen Sie sicher, dass sie verfügbar sind als der App-Benutzer:

bash
$ ssh myapp@your-server-ip
myapp@server:~$ node -v && npm -v

Begehen package.json und package-lock.json zu Ihrem Repository. Haken Sie den Build ein danach deploy:shared also .env ist verlinkt (Vite liest VITE_* Variablen von dort) und vor deploy:symlink also Kompilierte Assets sind in der Version vorhanden, bevor sie live geht.

Hängen Sie den Block unten an unten von /home/myapp/.deployer/deploy.php, unten die automatisch generierten Aufgabendefinitionen von Cipi:

php
// ── Custom: frontend build (safe zone — keep below Cipi-managed blocks) ──

task('npm:build', function () {
    cd('{{release_path}}');
    run('npm ci --no-audit --no-fund && npm run build');
});

// .env is linked → build assets → then migrations / optimize / symlink
after('deploy:shared', 'npm:build');

Dies entspricht dem Äquivalent von npm install && npm run build bei jedem Einsatz. Bevorzugen npm ci in der Produktion wann package-lock.json ist verpflichtet – es ist schneller und reproduzierbar. Benutzen npm install stattdessen nur, wenn Sie Abhängigkeiten nicht sperren.

Wenn Sie separate Installations- und Build-Schritte benötigen (z. B. zum Zwischenspeichern). node_modules über Veröffentlichungen hinweg), Teilen Sie sie in zwei Aufgaben auf:

php
task('npm:ci', function () {
    cd('{{release_path}}');
    run('npm ci --no-audit --no-fund');
});

task('npm:build', function () {
    cd('{{release_path}}');
    run('npm run build');
});

after('deploy:shared', 'npm:ci');
after('npm:ci', 'npm:build');

Um nachfolgende Bereitstellungen zu beschleunigen, können Sie Abhängigkeiten über Releases hinweg beibehalten, indem Sie sie hinzufügen node_modules in die freigegebenen Verzeichnisse des Deployers (optional – nur wenn Ihr Projekt dies unterstützt). es):

php
add('shared_dirs', ['node_modules']);

Bearbeiten Sie die Datei auf dem Server als App-Benutzer und testen Sie sie dann mit cipi deploy myapp:

bash
$ ssh myapp@your-server-ip
myapp@server:~$ nano ~/.deployer/deploy.php
# paste the custom tasks at the bottom, save, then as root:
$ cipi deploy myapp
Alternativ: laufen npm ci && npm run build in GitHub Aktionen oder GitLab CI vor Der SSH-Bereitstellungsschritt wird ausgeführt, sodass der Server nur vorgefertigte Assets erhält. Siehe CI/CD Pipelines – SSH-Bereitstellung.

Ausführen zusätzlicher artisan-Befehle

php
// Seed only in specific environments
task('artisan:db:seed', function () {
    run('{{bin/php}} {{release_path}}/artisan db:seed --force');
});
Cipi überschreibt möglicherweise „deploy.php“ wenn du rennst cipi app edit myapp --php=X oder cipi app edit myapp --branch=X. Sichern Ihre Anpassungen oder bewahren Sie sie in einem Abschnitt auf, der klar von den Cipi-verwalteten Blöcken getrennt ist. A Ein sicheres Muster besteht darin, alle benutzerdefinierten Aufgaben am Ende der Datei nach der Standardaufgabe zu platzieren Definition.

Deaktivieren eines Standardschritts

Um eine Aufgabe zu überspringen – beispielsweise wenn Sie Migrationen manuell durchführen – kommentieren Sie sie aus oder entfernen Sie sie aus der deploy Aufgabendefinition:

php
// Remove the migrate step from the pipeline
task('deploy', [
    'deploy:prepare',
    'deploy:vendors',
    'deploy:shared',
    // 'artisan:migrate',  ← disabled
    'artisan:optimize',
    'artisan:storage:link',
    'deploy:symlink',
    'cipi:restart-workers',
    'deploy:cleanup',
]);

Testen Sie Ihre Änderungen

Nach der Bearbeitung deploy.php, führen Sie immer eine Testbereitstellung durch, bevor Sie in die Produktion gehen:

bash
$ cipi deploy myapp

# If something goes wrong, instant rollback:
$ cipi deploy myapp --rollback

# If the deploy is stuck (e.g. interrupted mid-run):
$ cipi deploy myapp --unlock
Das Bereitstellungsprotokoll ist immer unter verfügbar ~/logs/deploy.log oder über cipi app logs myapp --type=deploy. Überprüfen Sie es zuerst, wenn Sie einen Fehler beheben bereitstellen.

Bereitstellen & CI/CD – Übersicht

Mit Cipi, CI (bauen und testen) und CD (Freigabe zur Produktion) können geteilt oder kombiniert werden. Letztendlich läuft jede Bereitstellung gleich ab Bereitsteller Pipeline auf dem Server – Klonen, composer install, Migrationen, Symlink-Tausch, Neustart des Arbeiters. Was sich ändert ist was auslöst diese Pipeline.

Cipi unterstützt zwei Triggermodelle. Beginnen Sie bei den meisten Laravel-Apps mit webhook + Cipi Agent Weg. Wechseln Sie zu einer vollständigen CI/CD-Pipeline, wenn Sie Gates, Backups usw. benötigen Infrastruktur-Orchestrierung, die ein einfacher Push-Hook nicht ausdrücken kann.

Zwei Möglichkeiten, eine Bereitstellung auszulösen

Webhook + Cipi Agent (empfohlen) CI/CD-Pipeline über SSH
Auslöser Git-Anbieter postet an /cipi/webhook auf Druck GitHub Aktionen / GitLab CI-Job wird ausgeführt cipi deploy über SSH
Serverzugriff von CI Keine – nur HTTPS für Ihre App-Domain Dedizierter SSH-Schlüssel, der als CI-Geheimnis gespeichert ist
Tests vor der Bereitstellung Lokal oder in einem separaten CI-Job ausführen; „Deploy“ wird immer noch beim Push ausgelöst, es sei denn, Sie deaktivieren es webhook Nativ – Der Bereitstellungsschritt wird erst danach ausgeführt needs: test (oder gleichwertige) Prüfungen
Backup vor Veröffentlichung Manuell oder cron auf dem Server Pipeline-Job – siehe sicher bereitstellen
Vorschau/Überprüfung von Apps Wird standardmäßig nicht unterstützt Pipeline erstellt pro Zweig Cipi Apps – siehe Vorschau Umgebungen
Komplexität der Einrichtung Niedrig – composer require cipi/agent + ein webhook Mittel – SSH-Schlüssel, Geheimnisse, Workflow YAML

Welchen Ansatz soll ich verwenden?

Anwendungsfall Empfohlener Ansatz Wo kann man mehr lesen?
Einzelne Laravel-App, Push-to-Deploy auf main Webhook + Agent Webhook-Setup
Nur bereitstellen, wenn die CI-Tests erfolgreich sind Pipeline-SSH (Produktion webhook deaktivieren) Pipeline-SSH-Bereitstellung
DB + Dateisicherung vor jeder Produktionsfreigabe Pipeline-SSH Sichere Bereitstellung mit Backup
Slack-/Telegram-Benachrichtigungen zum Bereitstellungsergebnis Pipeline-SSH Stellen Sie Benachrichtigungen bereit
Kurzlebige URL pro Funktionszweig (Apps bewerten) Pipeline-SSH Vorschauumgebungen
Stellen Sie mehrere Apps über ein Repository auf einem Server bereit Entweder – webhook pro App oder eine Pipeline mit Parallelität cipi deploy Multi-App-Bereitstellung
Wählen Sie einen Auslöser pro App. Lassen Sie eine Produktion webhook nicht gleichzeitig aktiv Ausführen von Pipeline-Bereitstellungen auf Push – Konflikt zwischen zwei gleichzeitigen Deployer-Ausführungen in der Sperrdatei. Benutzen cipi deploy myapp --unlock wenn ein festsitzendes Schloss zurückbleibt.

Automatische Bereitstellungen – Cipi Agent & webhook

cipi-Agent (cipi/agent) ist ein Laravel-Paket, das verfügbar macht POST /cipi/webhook in Ihrer laufenden Anwendung. Wenn GitHub oder GitLab einen Push sendet Bei diesem Ereignis validiert der Agent die Payload-Signatur, bestätigt sie sofort und stellt eine Bereitstellung in die Warteschlange der Server – kein SSH vom CI-Runner, nein sudo, keine offenen eingehenden Ports über HTTPS hinaus.

So funktioniert der webhook-Flow

Das Design trennt schnelle HTTP Bestätigung von langsamer Deployer Arbeit. Eine Bereitstellung kann mehrere Minuten dauern. Git-Anbieter überschreiten webhook HTTP Aufrufe nach ca. 10 Sekunden. Cipi löst dieses Problem mit einer Flag-Datei und der Crontab des App-Benutzers.

fließen
  Developer                    Git provider              Your Laravel app (Cipi Agent)           Server (app user cron)
      │                              │                              │                                        │
      │  git push main               │                              │                                        │
      │ ───────────────────────────► │                              │                                        │
      │                              │  POST /cipi/webhook          │                                        │
      │                              │  (signed with secret)        │                                        │
      │                              │ ───────────────────────────► │                                        │
      │                              │                              │ 1. Verify CIPI_WEBHOOK_TOKEN           │
      │                              │                              │ 2. Check branch (CIPI_DEPLOY_BRANCH)   │
      │                              │                              │ 3. Write ~/.deploy-trigger             │
      │                              │ ◄─────────────────────────── │ 4. Return 200 immediately              │
      │                              │                              │                                        │
      │                              │                              │         every minute (* * * * *)       │
      │                              │                              │ ◄──────────────────────────────────────│
      │                              │                              │         cron sees .deploy-trigger      │
      │                              │                              │         removes file, runs Deployer    │
      │                              │                              │         in background as app user      │
      │                              │                              │                                        │
      │                              │                              │         clone → composer → migrate     │
      │                              │                              │         → symlink swap → workers       │

Der Deployer wird immer als ausgeführt app Linux-Benutzer (z.B. myapp), mit dem Korrekte PHP-Binär- und Dateiberechtigungen – derselbe Kontext wie ein Handbuch cipi deploy myapp. Der webhook wird niemals direkt an den Deployer ausgezahlt; es lässt nur das fallen Triggerdatei, die von der Crontab von Cipi bereits überwacht wird.

Voraussetzungen

Anforderung Warum
Cipi App, erstellt mit Git-Repository Der Bereitstellungsschlüssel muss das Repo klonen – siehe Git-Auto-Setup
Mindestens eine erfolgreiche manuelle Bereitstellung Das Agentenpaket muss in der Datei vorhanden sein current Veröffentlichung vor dem webhook Route existiert
composer require cipi/agent im Projekt Registriert die /cipi/webhook Routen- und Signaturvalidierung
Webhook URL erreichbar über HTTPS Git-Anbieter benötigen eine öffentliche URL; verwenden cipi ssl install zuerst
CIPI_WEBHOOK_TOKEN in shared/.env Automatisch generiert um cipi app create; in allen Versionen geteilt

Schritt-für-Schritt-Einrichtung

1. Erstellen Sie die App und stellen Sie sie einmal manuell bereit damit der Server Ihr Repository klonen kann:

bash
$ cipi app create --user=myapp --domain=myapp.com \
    --repository=git@github.com:you/myapp.git --branch=main --php=8.5
$ cipi deploy myapp

2. Installieren Sie den Cipi Agent in Ihrem Laravel-Projekt lokal festschreiben und pushen:

bash
$ composer require cipi/agent
$ git add composer.json composer.lock
$ git commit -m "Add Cipi Agent for webhook deploys"
$ git push origin main
$ cipi deploy myapp   # one more manual deploy until webhook is live

3. Konfigurieren Sie webhook. Wenn Sie ein GitHub- oder GitLab-Token gespeichert haben, ist möglicherweise Cipi vorhanden webhook wurde bereits erstellt app create – erkundigen Sie sich bei cipi git status. Andernfalls rufen Sie die URL und das Geheimnis ab:

bash
$ cipi deploy myapp --webhook

Fügen Sie webhook in Ihrem Git-Anbieter hinzu:

Anbieter Payload-URL Geheimes Feld Veranstaltungen
GitHub https://myapp.com/cipi/webhook Geheimnis → Wert von --webhook Nur die push Ereignis
GitLab https://myapp.com/cipi/webhook Geheimer Token → gleicher Wert Push-Events

4. Beschränken Sie sich auf Ihren Bereitstellungszweig (für die Produktion empfohlen):

env
CIPI_DEPLOY_BRANCH=main

Stellen Sie dies ein shared/.env über cipi app env myapp. Pusht auf andere Zweige erhalten Sie eine skipped Antwort und keine Bereitstellungsläufe.

5. Überprüfen Sie. Pushen Sie ein kleines Commit main und sehen Sie sich das Bereitstellungsprotokoll an:

bash
$ cipi app logs myapp --type=deploy
# or on the server as the app user:
$ tail -f /home/myapp/logs/deploy.log

Innerhalb von etwa einer Minute nach der Zustellung von webhook sollte eine neue Deployer-Version erscheinen. Bestätigen Sie das Live-Commit mit php artisan cipi:status oder die Gesundheit überprüfen Endpunkt.

Git-Auto-Setup

Wenn ein GitHub oder GitLab Token ist auf dem Server konfiguriert, Cipi Registriert den Bereitstellungsschlüssel und erstellt den webhook automatisch bei jedem cipi app create. Die App-Zusammenfassung wird angezeigt „automatisch konfiguriert ✓“ statt manuell Anweisungen. Lebenszyklusereignisse (app edit --repository, app delete) behalten Schlüssel und Webhooks synchron.

Fehlerbehebung

Symptom Wahrscheinliche Ursache Beheben
Webhook gibt 404 zurück Agent noch nicht bereitgestellt Lauf cipi deploy myapp nach dem Hinzufügen cipi/agent zu composer.json
Webhook gibt 403 / ungültige Signatur zurück Geheimes Missverhältnis Token erneut kopieren von cipi deploy myapp --webhook in die Providereinstellungen ein
200 OK, aber keine Bereitstellung Branch wurde herausgefiltert Überprüfen CIPI_DEPLOY_BRANCH entspricht dem gepushten Zweig
Bereitstellung hängt/Sperrfehler Vorherige Bereitstellung unterbrochen cipi deploy myapp --unlock Versuchen Sie es dann erneut
Deploy wird zweimal auf einmal ausgeführt Webhook + Pipeline beide aktiv Deaktivieren Sie einen Auslöser – siehe Übersicht
Der Agent unterstützt auch manuelle und KI-ausgelöste Bereitstellungen über MCP deploy Werkzeug – es nutzt das Gleiche .deploy-trigger Mechanismus. Siehe Cipi Agent für Gesundheitschecks, MCP und Anonymisierungsfunktionen.

CI/CD Pipelines – SSH-Bereitstellung

Wenn das Modell webhook nicht ausreicht, führen Sie es aus GitHub Aktionen oder GitLab CI/CD Jobs, die per SSH auf den Server zugreifen und diese aufrufen cipi deploy. Das ist das Die richtige Wahl, wann immer der Einsatz erfolgen muss bedingt – auf Tests beschränkt, denen Backups vorausgehen, gefolgt von Benachrichtigungen oder der Orchestrierung neuer Vorschau-Apps.

Wenn Sie eine Pipeline anstelle eines webhook benötigen

  • Qualitätstor – laufen php artisan test, statische Analyse oder Frontend wird erstellt, bevor Code in Produktion geht
  • Sichere Freigabe — Snapshot der Datenbank und shared/ bis S3 vorher Tauschen des Symlinks (sicher bereitstellen)
  • Sichtbarkeit des Teams – Posten Sie Erfolg/Misserfolg an Slack oder Telegram mit aktiviertem Rollback Fehler (Benachrichtigungen bereitstellen)
  • Überprüfen Sie Apps – Erstellen oder aktualisieren Sie eine vollständige Cipi-App pro Feature-Zweig (Vorschauumgebungen)
  • Multi-App-Monorepo – bereitstellen frontend und api in parallel nach einem einzigen Testjob

Für diese Arbeitsabläufe gilt: Deaktivieren Sie die Produktion webhook (oder niemals eines erstellen) also nur Die Pipeline löst Bereitstellungen aus. Sie können weiterhin Cipi Agent in der App für Gesundheitsprüfungen und MCP verwenden.

SSH-Zugriff für CI

Generieren Sie eine dedizierte ed25519 Schlüsselpaar für den CI-Runner. Fügen Sie die hinzu öffentlicher Schlüssel zu /root/.ssh/authorized_keys auf dem Server (bzw cipi Benutzer, wenn Sie möchten sudo cipi deploy) und speichern Sie die privater Schlüssel als CI-Geheimnis. Verwenden Sie Git-Bereitstellungsschlüssel oder persönliche SSH-Schlüssel niemals wieder.
bash
# on your local machine
$ ssh-keygen -t ed25519 -C "ci-deploy" -f ~/.ssh/ci_deploy -N ""

# copy the public key to the server
$ ssh-copy-id -i ~/.ssh/ci_deploy.pub root@your-server-ip

# copy the private key content → add it as a CI secret (SERVER_SSH_KEY)
$ cat ~/.ssh/ci_deploy

Speichern SERVER_HOST (Server-IP oder Hostname) daneben SERVER_SSH_KEY in deinem Repository-Geheimnisse (GitHub) oder CI/CD Variablen (GitLab).

GitHub Aktionen – testen und dann bereitstellen

Fügen Sie den privaten Schlüssel als Repository-Geheimnis mit dem Namen hinzu SERVER_SSH_KEY und die Server-IP als SERVER_HOST.

yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: php artisan test

  deploy:
    runs-on: ubuntu-latest
    needs: test          # only deploy if tests pass
    steps:
      - name: Deploy via Cipi
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: cipi
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: sudo cipi deploy myapp

Für ein Rollback bei einem Fehler erweitern Sie den Skriptschritt:

yaml
          script: |
            sudo cipi deploy myapp || (sudo cipi deploy myapp --rollback && exit 1)

GitLab CI / CD

Fügen Sie den privaten Schlüssel als CI/CD-Variable mit dem Namen hinzu SERVER_SSH_KEY (Typ: Datei) und dem Server IP als SERVER_HOST.

yaml
# .gitlab-ci.yml
stages:
  - test
  - deploy

test:
  stage: test
  script:
    - php artisan test

deploy:
  stage: deploy
  environment: production
  only:
    - main
  before_script:
    - apt-get install -y openssh-client
    - eval $(ssh-agent -s)
    - echo "$SERVER_SSH_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts
  script:
    - ssh root@$SERVER_HOST "cipi deploy myapp"

Mit Rollback bei Fehler:

yaml
  script:
    - ssh root@$SERVER_HOST "cipi deploy myapp || (cipi deploy myapp --rollback && exit 1)"

Multi-App-Bereitstellung

Wenn dieselbe Pipeline mehrere Apps auf demselben Server verwaltet:

yaml
# GitHub Actions — deploy multiple apps in parallel
      - name: Deploy
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cipi deploy frontend &
            cipi deploy api &
            wait

Erweiterte Pipeline-Muster

Sobald die SSH-Bereitstellung funktioniert, fügen Sie diese Abschnitte zu einem einzigen Produktionsworkflow zusammen:

Muster Was die Pipeline hinzufügt Leitfaden
Benachrichtigungen Slack- oder Telegram-Nachricht bei Erfolg, Misserfolg und automatischem Rollback Stellen Sie Benachrichtigungen bereit
Sichere Bereitstellung cipi db backup + cipi backup run vor cipi deploy; Rollback bei Fehler Sichere Bereitstellung mit Backup
Vorschauumgebungen Erstellen/Aktualisieren/Löschen pro Zweig Cipi Apps mit Platzhalter DNS + SSL Vorschauumgebungen

Ein typisches ausgereiftes Setup verwendet das webhook für eine Staging-App (sofortiges Feedback zu jedem drücken) und a Pipeline für die Produktion (Tests → Backup → Bereitstellen → Benachrichtigen). Jede App hat Es handelt sich um einen eigenen Auslöser – sie geraten nie in Konflikt, da sie auf unterschiedliche Cipi-App-Benutzer abzielen.

Stellen Sie Benachrichtigungen bereit

Pipeline-Anwendungsfall: Der Pfad webhook wird stillschweigend bereitgestellt – Git gibt 200 und das Team zurück findet es nur heraus, wenn sie sich die Protokolle ansehen. Mit einem SSH-Pipeline, hinzufügen Benachrichtigungsschritte danachcipi deploy um Erfolg, Misserfolg und Automatik zu übertragen Rollbacks zu Slack oder Telegram. Beide Beispiele unten funktionieren mit GitHub-Aktionen und GitLab CI-Verwendung nur standardmäßige HTTP-Aufrufe – keine zusätzlichen Plattformabhängigkeiten.

Locker

Fügen Sie einen letzten Schritt hinzu, der unabhängig vom Bereitstellungsergebnis in einem Slack webhook postet. Benutzen if: always() in GitHub-Aktionen, sodass die Benachrichtigung sowohl bei Erfolg als auch bei Misserfolg ausgelöst wird.

Erstellen Sie eine Eingehend Webhook in Ihrem Slack-Arbeitsbereich und speichern Sie die URL unter SLACK_WEBHOOK_URL in Ihren CI-Geheimnissen.

yaml
# GitHub Actions — deploy + Slack notification
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        id: deploy
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: cipi deploy myapp

      - name: Notify Slack — success
        if: success()
        uses: slackapi/slack-github-action@v2
        with:
          webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
          webhook-type: incoming-webhook
          payload: |
            {
              "text": ":white_check_mark: *myapp* deployed successfully",
              "attachments": [{
                "color": "good",
                "fields": [
                  { "title": "Branch",  "value": "${{ github.ref_name }}", "short": true },
                  { "title": "By",      "value": "${{ github.actor }}",    "short": true },
                  { "title": "Commit",  "value": "${{ github.sha }}",      "short": false }
                ]
              }]
            }

      - name: Notify Slack — failure
        if: failure()
        uses: slackapi/slack-github-action@v2
        with:
          webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
          webhook-type: incoming-webhook
          payload: |
            {
              "text": ":x: *myapp* deploy FAILED — rolling back",
              "attachments": [{
                "color": "danger",
                "fields": [
                  { "title": "Branch", "value": "${{ github.ref_name }}", "short": true },
                  { "title": "By",     "value": "${{ github.actor }}",    "short": true },
                  { "title": "Run",    "value": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", "short": false }
                ]
              }]
            }

      - name: Rollback on failure
        if: failure()
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: cipi deploy myapp --rollback

Verwenden Sie für GitLab CI curl direkt – kein Plugin erforderlich:

yaml
# .gitlab-ci.yml — deploy stage with Slack notification
deploy:
  stage: deploy
  script:
    - ssh root@$SERVER_HOST "cipi deploy myapp" && export DEPLOY_STATUS="success" || export DEPLOY_STATUS="failed"
    - |
      if [ "$DEPLOY_STATUS" = "success" ]; then
        curl -s -X POST "$SLACK_WEBHOOK_URL" \
          -H "Content-Type: application/json" \
          -d "{\"text\":\":white_check_mark: *myapp* deployed by $GITLAB_USER_LOGIN on \`$CI_COMMIT_REF_NAME\`\"}"
      else
        curl -s -X POST "$SLACK_WEBHOOK_URL" \
          -H "Content-Type: application/json" \
          -d "{\"text\":\":x: *myapp* deploy FAILED — <$CI_PIPELINE_URL|view pipeline>\"}"
        ssh root@$SERVER_HOST "cipi deploy myapp --rollback"
        exit 1
      fi

Telegramm

Erstellen Sie einen Telegram-Bot über @BotFather, holen Sie sich das Bot-Token und suchen Sie Ihre Chat-/Gruppen-ID. Speichern Sie sie als TELEGRAM_BOT_TOKEN und TELEGRAM_CHAT_ID in CI-Geheimnissen.

yaml
# GitHub Actions — deploy + Telegram notification
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy
        id: deploy
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: cipi deploy myapp

      - name: Notify Telegram — success
        if: success()
        run: |
          curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
            -d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \
            -d parse_mode="Markdown" \
            -d text="✅ *myapp* deployed successfully%0ABranch: \`${{ github.ref_name }}\`%0ABy: ${{ github.actor }}"

      - name: Notify Telegram — failure + rollback
        if: failure()
        run: |
          curl -s -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
            -d chat_id="${{ secrets.TELEGRAM_CHAT_ID }}" \
            -d parse_mode="Markdown" \
            -d text="❌ *myapp* deploy FAILED — rolling back%0ABranch: \`${{ github.ref_name }}\`%0A[View run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})"
          ssh -o StrictHostKeyChecking=no -i <(echo "${{ secrets.SERVER_SSH_KEY }}") \
            root@${{ secrets.SERVER_HOST }} "cipi deploy myapp --rollback"

GitLab CI-Äquivalent (rein). curl, keine zusätzlichen Abhängigkeiten):

yaml
# .gitlab-ci.yml — deploy stage with Telegram notification
deploy:
  stage: deploy
  script:
    - ssh root@$SERVER_HOST "cipi deploy myapp" && RESULT="✅ deployed" || RESULT="❌ FAILED"
    - |
      curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
        -d chat_id="$TELEGRAM_CHAT_ID" \
        -d parse_mode="Markdown" \
        -d text="*myapp* ${RESULT}%0ABranch: \`$CI_COMMIT_REF_NAME\`%0ABy: $GITLAB_USER_LOGIN"
    - |
      if echo "$RESULT" | grep -q "FAILED"; then
        ssh root@$SERVER_HOST "cipi deploy myapp --rollback"
        exit 1
      fi
Um Ihre Telegram-Chat-ID zu finden, fügen Sie den Bot zur Zielgruppe/zum Kanal hinzu und rufen Sie dann an https://api.telegram.org/bot<TOKEN>/getUpdates und suche nach dem chat.id Feld in der Antwort. Für private Chats schreiben Sie einfach zuerst eine Nachricht an den Bot.

Sichere Bereitstellung – Sicherung vor der Veröffentlichung

Für einen integrierten Dump unmittelbar vor der Ausführung des Deployers (keine Pipeline-Phase erforderlich) verwenden Sie --snapshot / --snapshot-requiredoder aktivieren cipi app edit <app> --predeploy-snapshot.

Pipeline-Anwendungsfall: Eine webhook-Bereitstellung kann vor der Codefreigabe keinen Sicherungsschritt ausführen – Das Push-Ereignis wird sofort bereitgestellt. In einem CI/CD-Pipeline, füge a hinzu gewidmet backup Phase, die vorher gelingen muss deploy beginnt. A Ein produktionstauglicher Workflow sollte immer einen Wiederherstellungspunkt erstellen vor Der neue Code geht leben. Cipi bietet zwei komplementäre Sicherungsbefehle, die zwei verschiedenen Sicherheitsstufen zugeordnet sind:

bash
# local DB snapshot — fast, on-disk, instant rollback
$ cipi db backup myapp
# → /var/log/cipi/backups/myapp_20260303_143012.sql.gz

# S3 backup — DB dump + shared/ folder uploaded to your bucket
$ cipi backup run myapp
# → s3://your-bucket/cipi/myapp/2026-03-03_143015/db.sql.gz
# → s3://your-bucket/cipi/myapp/2026-03-03_143015/shared.tar.gz

Wenn sie zusammen in einer Pipeline verwendet werden, erhalten Sie sowohl einen schnellen lokalen Wiederherstellungspunkt als auch eine Off-Server-Kopie davon die Datenbank und alle hochgeladenen Dateien. Die Bereitstellung beginnt nur, wenn beide Sicherungen erfolgreich sind.

Voraussetzung: laufen cipi backup configure einmal auf dem Server zu Verknüpfen Sie zuvor Ihre S3-Anmeldeinformationen cipi backup run verwendet werden kann. cipi db backup funktioniert ohne jegliche Konfiguration – es ist immer verfügbar.

Was jeder Befehl intern bewirkt

cipi db backup <app> Anrufe mysqldump --single-transaction --routines --triggers und komprimiert die Ausgabe in gzip /var/log/cipi/backups/<app>_<timestamp>.sql.gz. Die Datei bleibt auf dem Server und wird nie automatisch gelöscht – fügen Sie einen Bereinigungsschritt oder einen cron hinzu, wenn der Speicherplatz wichtig ist.

cipi backup run <app> macht zwei Dinge: Dump der Datenbank mit mariadb-dump --single-transaction in ein temporäres Verzeichnis und archiviert das Ganze /home/<app>/shared/ Ordner (der enthält .env, storage/und alle vom Benutzer hochgeladenen Dateien). Beide Archive werden dann unter S3 hochgeladen Pfad cipi/<app>/<timestamp>/. Nach erfolgreichem Abschluss werden die temporären Dateien gelöscht hochladen.

GitHub Aktionen – sicherer Bereitstellungsworkflow

yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: php artisan test

  backup:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - name: Local DB backup
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: cipi db backup myapp

      - name: S3 backup (DB + shared)
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: cipi backup run myapp

  deploy:
    runs-on: ubuntu-latest
    needs: backup          # only runs if backup job succeeds
    steps:
      - name: Deploy
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: cipi deploy myapp

      - name: Rollback on failure
        if: failure()
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            cipi deploy myapp --rollback
            echo "Deploy failed — rolled back to previous release"

Das Jobdiagramm erzwingt die Reihenfolge: testbackupdeploy. Wenn Wenn ein Auftrag fehlschlägt, werden die nachfolgenden übersprungen. Wenn der Bereitstellungsschritt selbst fehlschlägt, wird der rollback Schritt wird automatisch ausgelöst und stellt die vorherige Deployer-Version wieder her.

GitLab CI/CD – sichere Bereitstellungspipeline

yaml
# .gitlab-ci.yml
stages:
  - test
  - backup
  - deploy

variables:
  APP: myapp

.ssh: &ssh
  before_script:
    - apt-get install -y openssh-client
    - eval $(ssh-agent -s)
    - echo "$SERVER_SSH_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - ssh-keyscan -H "$SERVER_HOST" >> ~/.ssh/known_hosts

test:
  stage: test
  script: php artisan test
  only: [main]

backup-local:
  stage: backup
  <<: *ssh
  only: [main]
  script:
    - ssh root@$SERVER_HOST "cipi db backup $APP"

backup-s3:
  stage: backup
  <<: *ssh
  only: [main]
  script:
    - ssh root@$SERVER_HOST "cipi backup run $APP"

deploy:
  stage: deploy
  <<: *ssh
  only: [main]
  script:
    - |
      ssh root@$SERVER_HOST "
        cipi deploy $APP || {
          cipi deploy $APP --rollback
          echo 'Deploy failed — rolled back'
          exit 1
        }
      "
  after_script:
    - echo "Released → https://myapp.com"

backup-local und backup-s3 befinden sich in der gleichen Phase und laufen daher parallel, wenn Sie haben mehrere Läufer, was die gesamte Pipeline-Zeit verkürzt. Beides muss vor dem Erfolg gelingen deploy Etappe beginnt.

Wiederherstellung aus lokaler Sicherung

Wenn Sie die Datenbank auf den Snapshot zurücksetzen müssen, der unmittelbar vor der Bereitstellung erstellt wurde:

bash
# list available local snapshots
$ ls -lh /var/log/cipi/backups/myapp_*.sql.gz

# restore the most recent one
$ cipi db restore myapp /var/log/cipi/backups/myapp_20260303_143012.sql.gz

# also roll back the code release
$ cipi deploy myapp --rollback

Wiederherstellung aus S3-Backup

bash
# list available S3 snapshots for this app
$ cipi backup list myapp

# download the DB snapshot from S3
$ aws s3 cp s3://your-bucket/cipi/myapp/2026-03-03_143015/db.sql.gz /tmp/db.sql.gz

# restore the database
$ cipi db restore myapp /tmp/db.sql.gz

# (optional) restore shared/ files
$ aws s3 cp s3://your-bucket/cipi/myapp/2026-03-03_143015/shared.tar.gz /tmp/shared.tar.gz
$ tar -xzf /tmp/shared.tar.gz -C /home/myapp/
Lokale Backups werden niemals automatisch gelöscht. Mit jeder Bereitstellung wird ein neues hinzugefügt .sql.gz Datei an /var/log/cipi/backups/. Bei einem vollen Einsatzplan Fügen Sie eine Bereinigung cron hinzu oder behalten Sie nur die letzten N Dateien:

ls -t /var/log/cipi/backups/myapp_*.sql.gz | tail -n +6 | xargs rm -f

In diesem Beispiel werden die fünf aktuellsten Snapshots beibehalten und ältere gelöscht.

Vorschauumgebungen (Bereitstellung pro Zweig)

Pipeline-Anwendungsfall: Webhooks verweisen auf eine einzelne Produktions-URL – sie können keine erstellen neue Cipi App pro Zweig. Vorschauumgebungen erfordern a CI/CD Pipeline dass eine SSH-Verbindung zum Server hergestellt wird, ein deterministischer App-Name aus dem Zweig berechnet wird und läuft cipi app create oder cipi deploy entsprechend. Jede Nichtproduktion Zweig kann seine eigene Live-URL erhalten – eine vollständig bereitgestellte Laravel-App mit eigener Datenbank, eigenen Workern und HTTPS. Dieses Muster wird manchmal als „Rezensions-Apps“ oder „ephemere Umgebungen“ bezeichnet.

Das URL-Format verwendet drei durch Bindestriche getrennte Slugs, sodass jede Umgebung für Menschen lesbar ist Weltweit einzigartig:

Beispiel-URLs
https://develop-acmeco-3a1f9c2e.preview.domain.ltd
https://release-1-2-3-acmeco-3a1f9c2e.preview.domain.ltd
https://main-acmeco-3a1f9c2e.preview.domain.ltd

Wie die Bezeichner generiert werden

Zur Pipeline-Laufzeit werden drei Werte abgeleitet:

bash
# branch name → lowercase, non-alphanum → hyphens, trim edges
BRANCH_SLUG=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]' \
  | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//')

# repo/project name → same treatment
PROJECT_SLUG=$(echo "$PROJECT" | tr '[:upper:]' '[:lower:]' \
  | sed 's/[^a-z0-9]/-/g')

# deterministic MD5 hash — same branch always gets the same environment
HASH=$(echo -n "${BRANCH_SLUG}${PROJECT_SLUG}" | md5sum | cut -c1-8)

# Cipi app username: must be lowercase alphanumeric, 3–32 chars, no hyphens
# hex chars (0–9, a–f) are valid; prefix "pr" ensures it starts with a letter
APP_NAME="pr${HASH}"    # e.g. pr3a1f9c2e

# human-readable domain with wildcard base
DOMAIN="${BRANCH_SLUG}-${PROJECT_SLUG}-${HASH}.${DEPLOY_WILDCARD_DOMAIN}"

Voraussetzungen (einmalige Servereinrichtung)

1. Platzhalter DNS – füge ein hinzu A aufzeichnen *.preview.domain.ltd → <server-ip> bei Ihrem DNS-Anbieter. Alle Subdomains automatisch auflösen; Es sind keine DNS-Änderungen pro Zweig erforderlich.

2. Wildcard-Zertifikat SSL – Erhalten Sie einmal ein Wildcard-Zertifikat über die DNS-01-Herausforderung und installieren Sie es auf dem Server. Siehe die Wildcard-Domains Abschnitt für Anweisungen. Der Zertifikatspfad wird von den folgenden Pipeline-Beispielen verwendet /etc/letsencrypt/live/preview.domain.ltd/.

3. Repository-Zugriff – Die Pipeline-Beispiele verwenden eine HTTPS-URL mit einem persönlichen Das Zugriffstoken (PAT) ist eingebettet, sodass kein SSH-Bereitstellungsschlüssel pro App eingerichtet werden muss. Der Token benötigt nur lesen Zugriff auf das Repository.

GitHub Aktionen

Fügen Sie diese Geheimnisse zum Repository hinzu: SERVER_HOST, SERVER_SSH_KEY, DEPLOY_WILDCARD_DOMAIN (z.B. preview.domain.ltd), GH_PAT (a feinkörniges PAT mit Lesezugriff auf das Repo).

yaml
# .github/workflows/preview.yml
name: Preview

on:
  push:
    branches-ignore: [main, master]   # main branch uses your production pipeline
  delete:                              # clean up when a branch is deleted

jobs:
  deploy:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    steps:
      - name: Compute identifiers
        id: ids
        run: |
          BRANCH_SLUG=$(echo "${{ github.ref_name }}" \
            | tr '[:upper:]' '[:lower:]' \
            | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//')
          PROJECT_SLUG=$(echo "${{ github.event.repository.name }}" \
            | tr '[:upper:]' '[:lower:]' \
            | sed 's/[^a-z0-9]/-/g')
          HASH=$(echo -n "${BRANCH_SLUG}${PROJECT_SLUG}" | md5sum | cut -c1-8)
          APP_NAME="pr${HASH}"
          DOMAIN="${BRANCH_SLUG}-${PROJECT_SLUG}-${HASH}.${{ secrets.DEPLOY_WILDCARD_DOMAIN }}"
          REPO="https://oauth2:${{ secrets.GH_PAT }}@github.com/${{ github.repository }}.git"
          echo "app_name=${APP_NAME}"   >> "$GITHUB_OUTPUT"
          echo "domain=${DOMAIN}"       >> "$GITHUB_OUTPUT"
          echo "repo_url=${REPO}"       >> "$GITHUB_OUTPUT"

      - name: Create or update preview
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            APP="${{ steps.ids.outputs.app_name }}"
            DOMAIN="${{ steps.ids.outputs.domain }}"
            REPO="${{ steps.ids.outputs.repo_url }}"
            BRANCH="${{ github.ref_name }}"
            WILDCARD="/etc/letsencrypt/live/${{ secrets.DEPLOY_WILDCARD_DOMAIN }}"

            if cipi app show "$APP" &>/dev/null; then
              echo "→ Updating: $APP"
              cipi deploy "$APP"
            else
              echo "→ Creating: $APP → $DOMAIN"
              cipi app create \
                --user="$APP" \
                --domain="$DOMAIN" \
                --repository="$REPO" \
                --branch="$BRANCH" \
                --php=8.5

              # Patch nginx to listen on 443 using the pre-installed wildcard cert
              awk -v cert="$WILDCARD" '
                /^    listen 80;/ {
                  print
                  print "    listen 443 ssl http2;"
                  print "    ssl_certificate " cert "/fullchain.pem;"
                  print "    ssl_certificate_key " cert "/privkey.pem;"
                  next
                }
                { print }
              ' "/etc/nginx/sites-available/$APP" > /tmp/_cipi_vhost \
                && mv /tmp/_cipi_vhost "/etc/nginx/sites-available/$APP"
              nginx -t && systemctl reload nginx

              cipi deploy "$APP"
            fi

      - name: Print preview URL
        run: |
          echo ""
          echo "  Preview → https://${{ steps.ids.outputs.domain }}"
          echo ""

  cleanup:
    if: github.event_name == 'delete'
    runs-on: ubuntu-latest
    steps:
      - name: Compute identifiers
        id: ids
        run: |
          BRANCH_SLUG=$(echo "${{ github.event.ref }}" \
            | tr '[:upper:]' '[:lower:]' \
            | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//')
          PROJECT_SLUG=$(echo "${{ github.event.repository.name }}" \
            | tr '[:upper:]' '[:lower:]' \
            | sed 's/[^a-z0-9]/-/g')
          HASH=$(echo -n "${BRANCH_SLUG}${PROJECT_SLUG}" | md5sum | cut -c1-8)
          echo "app_name=pr${HASH}" >> "$GITHUB_OUTPUT"

      - name: Delete preview
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: root
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            APP="${{ steps.ids.outputs.app_name }}"
            if cipi app show "$APP" &>/dev/null; then
              echo "y" | cipi app delete "$APP"
              echo "→ Deleted: $APP"
            else
              echo "→ Not found, nothing to delete"
            fi

GitLab CI/CD

Fügen Sie diese CI/CD-Variablen hinzu: SERVER_HOST, SERVER_SSH_KEY (Dateityp), DEPLOY_WILDCARD_DOMAIN, GL_TOKEN (ein Projekt-/Gruppenzugriffstoken mit read_repository Geltungsbereich).

yaml
# .gitlab-ci.yml
stages:
  - preview
  - cleanup

.ssh_setup: &ssh_setup
  before_script:
    - apt-get install -y openssh-client
    - eval $(ssh-agent -s)
    - echo "$SERVER_SSH_KEY" | tr -d '\r' | ssh-add -
    - mkdir -p ~/.ssh
    - ssh-keyscan -H "$SERVER_HOST" >> ~/.ssh/known_hosts

.compute_ids: &compute_ids |
  BRANCH_SLUG=$(echo "$CI_COMMIT_REF_NAME" \
    | tr '[:upper:]' '[:lower:]' \
    | sed 's/[^a-z0-9]/-/g; s/--*/-/g; s/^-//; s/-$//')
  PROJECT_SLUG=$(echo "$CI_PROJECT_NAME" \
    | tr '[:upper:]' '[:lower:]' \
    | sed 's/[^a-z0-9]/-/g')
  HASH=$(echo -n "${BRANCH_SLUG}${PROJECT_SLUG}" | md5sum | cut -c1-8)
  APP="pr${HASH}"
  DOMAIN="${BRANCH_SLUG}-${PROJECT_SLUG}-${HASH}.${DEPLOY_WILDCARD_DOMAIN}"
  REPO="https://oauth2:${GL_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
  WILDCARD="/etc/letsencrypt/live/${DEPLOY_WILDCARD_DOMAIN}"

deploy-preview:
  stage: preview
  <<: *ssh_setup
  except:
    - main
    - master
  script:
    - *compute_ids
    - |
      ssh root@$SERVER_HOST bash -s << ENDSSH
        APP="$APP"
        DOMAIN="$DOMAIN"
        REPO="$REPO"
        BRANCH="$CI_COMMIT_REF_NAME"
        WILDCARD="$WILDCARD"

        if cipi app show "\$APP" &>/dev/null; then
          echo "Updating: \$APP"
          cipi deploy "\$APP"
        else
          echo "Creating: \$APP → \$DOMAIN"
          cipi app create \
            --user="\$APP" \
            --domain="\$DOMAIN" \
            --repository="\$REPO" \
            --branch="\$BRANCH" \
            --php=8.5

          awk -v cert="\$WILDCARD" '
            /^    listen 80;/ {
              print
              print "    listen 443 ssl http2;"
              print "    ssl_certificate " cert "/fullchain.pem;"
              print "    ssl_certificate_key " cert "/privkey.pem;"
              next
            }
            { print }
          ' "/etc/nginx/sites-available/\$APP" > /tmp/_cipi_vhost \
            && mv /tmp/_cipi_vhost "/etc/nginx/sites-available/\$APP"
          nginx -t && systemctl reload nginx

          cipi deploy "\$APP"
        fi
      ENDSSH
    - echo "Preview → https://$DOMAIN"

cleanup-preview:
  stage: cleanup
  <<: *ssh_setup
  only:
    - branches
  when: manual                    # or trigger on MR merge via rules:
  script:
    - *compute_ids
    - |
      ssh root@$SERVER_HOST "
        APP='$APP'
        if cipi app show \"\$APP\" &>/dev/null; then
          echo 'y' | cipi app delete \"\$APP\"
        fi
      "
In GitLab können Sie auslösen cleanup-preview automatisch, wenn eine Zusammenführungsanforderung vorliegt durch Hinzufügen von a zusammengeführt rules: Block, der prüft $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train" oder mit einem dedizierten workflow: mit if: $CI_PIPELINE_SOURCE == "merge_request_event".

Hinweise und Grenzen

Jede Vorschau-App ist eine vollständige Cipi-App – Es erhält einen eigenen Linux-Benutzer, eine eigene Datenbank und ein eigenes FPM pool, Supervisor Worker und Crontab. Bei einem kleinen VPS sammelt sich das schnell an. Lauf cipi app list regelmäßig überprüfen und veraltete Vorschauen löschen.

Der Patch nginx SSL ist nicht idempotent – wenn die Pipeline läuft cipi app create zweimal (z. B. aufgrund eines Wiederholungsversuchs), die awk Patch wird sein erneut angewendet. Der Hash sorgt dafür APP_NAME ist deterministisch, also die if cipi app show Der Schutz verhindert unter normalen Bedingungen eine Doppelerstellung.

Vermeiden Sie Laufen cipi ssl install in einer Vorschau-App – das wird es Überschreiben Sie die Wildcard-Zertifikatkonfiguration mit einem domänenspezifischen Let's Encrypt-Zertifikat, das fehlschlägt (das Die Domain hat keinen dedizierten DNS-Eintrag, nur den Platzhalter.