Bereitstellen & CI/CD
cipi deploy
Cipi Nutzungen 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.
cipi deploy und cipi deploy --rollbackAbbruch 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 / composer Aliase in den App-Benutzern .bashrc.
- Warteschlangenarbeiter stoppen (
cipi worker stop) - Klonen Sie das Repo in
releases/N/ - Lauf
composer install --no-dev(mit App's PHP) - Link
shared/.envundshared/storage/ - Lauf
artisan migrate --force - Lauf
artisan optimize - Lauf
artisan storage:link - Tauschen
currentsymlink atomically - Starten Sie die Warteschlangenarbeiter neu
- Alte Veröffentlichungen bereinigen (die letzten 5 behalten)
$ cipi deploy myapp # deploy latest commit $ cipi deploy myapp --rollback # instant rollback to previous release $ cipi deploy myapp --releases # list releases with date, commit and subject (v5.1.0+) $ cipi deploy myapp --log # timestamped deploy log (v5.1.0+) $ cipi deploy myapp --log=200 # last 200 lines of it $ 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 --rollback-on-unhealthy # v5.1.0+ undo a release that fails its healthcheck $ 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)
Wissen, ob eine Bereitstellung funktioniert hat (v5.1.0+)
Bis 5.0.x konnte eine fehlgeschlagene Bereitstellung stillschweigend ablaufen: der Fehlerzweig, seine Warnungen, das Rollback
Hinweis und die deploy_fail E-Mail war nicht erreichbarer Code, da ein Exit ungleich Null von
Der Deployer hat das getötet cipiProzess vor Ort. Seitv5.1.0:
- Bei beiden Wegen wird bei Erfolg und Misserfolg eine E-Mail gesendet — das CLI und das automatische Git
webhook gleich. Die Themen sind sichtlich unterschiedlich
(
Cipi deploy succeeded: myapp release 55 …/Cipi deploy FAILED: myapp …), und das Gremium benennt den Zweig, die Release-Nummer, der Commit-Hash und der Betreff, sein Autor und Datum, die Dauer, die vorherige Veröffentlichung, die Bereitstellung Protokollpfad und das Urteil der Integritätsprüfung nach der Bereitstellung. cipi deployExits ungleich Null, wenn die Bereitstellung fehlgeschlagen ist, also CI und Webhooks können es sehen. Gleiches gilt fürcipi deploy --rollback.- Die Erfolgs-E-Mail wird gesendet danach Überprüfung nach der Bereitstellung, also kann es nie kündigen Sie eine erfolgreiche Bereitstellung an, während die Site 500 zurückgibt.
Lesen des Bereitstellungsprotokolls
/home/<app>/logs/deploy.log Früher war die rohe Deployer-Ausgabe für immer angehängt, was
machte eine Bereitstellung, die über Nacht fehlschlug, anschließend unlesbar. Seitdem v5.1.0 jede Zeile
ist mit einem Zeitstempel versehen und jeder Lauf wird von einem Banner mit dem Namen des Auslösers (CLI oder webhook) eingeklammert
Zweig, die Veröffentlichung und die Dauer.
$ cipi deploy myapp --log=100 # same as tailing /home/myapp/logs/deploy.log $ cipi deploy myapp --releases # release number, date, commit, subject
Release-Verzeichnisse bleiben numerisch – Rollback hängt von dieser Reihenfolge ab – also --releases
Fügt das menschliche Detail hinzu, anstatt etwas umzubenennen.
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 nutzen das
laravel-octane.php Deployer-Vorlage (bei Bereitstellung neu laden/neustarten); Knoten aktivieren
baut mit cipi app edit <app> --node-build='…'.
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.
$ 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
- Bevor der Deployer startet, erstellt Cipi einen DB-Dump
/var/log/cipi/backups/. - Mit
--snapshot-required, ein fehlgeschlagener Dump (oder eine fehlende DB-Engine) Blöcke die Bereitstellung. - Mit
--snapshotAllein bei einem fehlgeschlagenen Dump wird eine Warnung und die Bereitstellung ausgegeben geht weiter.
Bei jeder Bereitstellung aktivieren
Schalten Sie es für eine App dauerhaft ein, damit bei jeder Bereitstellung (CLI, webhook oder Pipeline) zuerst ein Snapshot erstellt wird:
$ cipi app edit shop --predeploy-snapshot
cipi deploy --rollback stellt das Vorherige wieder her Code Nur Freigabe.
Das tut esnicht 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 Freigabe, 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 zur Bearbeitung von Freiform-Dateien.
$ 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.
cipi.yml – Konfiguration, die mit dem Code übertragen wird
Seitdem verfügbar v5.1.0. Eine App kann eine tragen cipi.yml Datei in seiner
Repository, das den erwarteten Zustand beschreibt: Domain-Aliase, PHP-Version
und Einstellungen, zusätzliche Datenbanken, queue workers oder Horizon, Reverb, die
Planer, es Gesundheitscheck und es ist Sicherung
Strategie. Die Datei befindet sich neben dem Code, sodass die Serverkonfiguration überprüft wird.
versioniert und versendet wie alles andere.
Befehle
$ cipi yml generate myapp # this app's current config, as a cipi.yml $ cipi yml example myapp # blank commented template, in myapp's namespace $ cipi yml validate myapp # parse and check, change nothing $ cipi yml plan myapp # show exactly what would change $ cipi yml apply myapp [--yes] # apply it $ cipi yml auto myapp on|off|status # apply after every successful deploy
Die Datei wird in nachgeschlagen current/cipi.yml, dann current/cipi.yaml, dann
shared/cipi.yml – überschreiben mit --file=<path>.
Beginnen Sie mit dem, was der Server bereits hat
Sie müssen es nicht von Hand schreiben. cipi yml generate <app> druckt die App's
Konfiguration, wie sie auf dem Server vorliegt – Aliase, PHP-Version und Einstellungen pro App, Extras
Datenbanken, seine Warteschlangenarbeiter (aus Supervisor zurücklesen), den Scheduler und die Sicherungsprofile
besitzt – als bereit zum Festschreiben.
$ cipi yml generate myapp > cipi.yml # then commit it $ cipi yml plan myapp # reports nothing to do
Cron Ausdrücke werden als freundlicher zurückgegeben every: 30m Form, wo sie sauber abgebildet werden,
Werte werden überall dort angegeben, wo ein einfacher Skalar falsch gelesen werden würde, und das Ergebnis wird durch das eingespeist
Überprüfen Sie vor dem Drucken den Validator. Serverweite Backup-Profile und die App-eigene Datenbank sind bewusst vorhanden
weggelassen – diese bleiben deins.
Die Datei
version: 1 app: # 8.3, 8.4 or 8.5 — must already be installed (cipi php install 8.5) php: "8.5" # The declared list replaces the current aliases: one you remove here is # removed from the server. The primary domain is not managed here. aliases: - „www.myapp.com“ - "*.myapp.com" # wildcard, for multi-tenant subdomains # Per-app php.ini overrides. Server-wide values stay with `cipi ini set`. ini: upload_max_filesize: 50M post_max_size: 60M memory_limit: 512M # Extra databases beyond the one created with the app. Credentials land in # /home/myapp/shared/cipi-databases.env — never written back to the repo. databases: - name: myapp_reporting - name: myapp_analytics engine: pgsql # mariadb (default) or pgsql workers: horizon: false # true replaces the queue workers below # Laravel Reverb (5.1.2+). Cipi allocates a localhost port, adds the Supervisor # program, proxies /app/{key} and /apps/{id}/… on this app's own domain, and # generates REVERB_APP_ID/KEY/SECRET plus the VITE_ copies in the .env. Laravel # apps only — declaring it on a --custom app is refused. reverb: false queues: - queue: default processes: 2 - queue: emails processes: 1 tries: 5 timeout: 300 # Laravel scheduler (* * * * * artisan schedule:run) schedule: true # HTTP healthcheck. Probed every 5 minutes and right after every deploy. # The URL must be one of this app's own domains. health: url: „https://myapp.com/up“ expect: 200 # grace: 8 # seconds before the first probe after a deploy # postdeploy: false # skip the check right after a deploy # rollback_on_unhealthy: true # undo a release that fails the check # # (the code symlink only — migrations are NOT undone) # Backup strategy for this app. Profile names must be myapp or myapp-*. backup: profiles: # Frequent and cheap: databases only, without the noisy tables. - name: myapp-db scope: db databases: [„meine App“, „meineapp_*“, „mieter_*“] exclude_tables: [„*.jobs“, „*.telescope_*“] every: 30m # 5m/10m/15m/20m/30m, 1h..12h, 1d..28d keep: 48 # keep the last 48 runs destinations: [local] # Slower, complete, off-site and encrypted. - name: myapp-nightly scope: all # all | files | db cron: "0 2 * * *" keep_days: 14 destinations: [s3] encrypt: true
cipi yml example nimmt einen optionalen App-Namen an – cipi yml example myapp
– Die Platzhalterdatenbanken und -profile landen also im Namespace dieser App und in der Vorlage
validiert, wie es ist.Bereitstellungen ignorieren die Datei, bis Sie sich dafür entscheiden
Bei der Bereitstellung passiert nichts, bis Sie es ausführen cipi yml auto <app> on. Mit diesem Opt-in
gegeben, jeder erfolgreich Die Bereitstellung stimmt ab – von beiden cipi deploy und
das Git webhook, letzteres durch eine eng begrenzte Sudoers-Regel. Eine Veröffentlichung, die keine trägt
cipi.yml ist ein stilles No-Op, und eine Datei, die die Validierung nicht besteht, wird per E-Mail gemeldet
(yml_fail) und nie teilweise angewendet. Eine erfolgreiche Abstimmung wird ausgelöst
yml_apply.
Warum es sicher ist, Git zu akzeptieren
Die Datei kommt aus einem Repository, sodass jeder, der einen Commit durchführen kann, ihren Inhalt kontrolliert. Es ist also so durchgehend gesperrt:
- It can only konfigurieren eine App, die bereits existiert – niemals erstellen, umbenennen oder einen löschen.
- Seine Datenbanken müssen benannt werden
<app>oder<app>_*, und es ist Backup-Profile<app>oder<app>-*. - Die URL zur Gesundheitsprüfung muss in eine der eigenen Domänen der App aufgelöst werden – andernfalls könnte ein Commit zum Ziel führen den Fünf-Minuten-Prober des Servers an einer internen Adresse und liest die Antwort aus der Warnung zurück E-Mails.
- Unbekannte Schlüssel sind Fehler und kein Feld enthält einen Shell-Befehl oder einen einzuschließenden Pfad.
- Der Parser implementiert eine bewusst kleine Teilmenge von YAML und lehnt Anker, Aliase, Tags und Merge ab Schlüssel, Blockskalare und Flusszuordnungen vollständig.
yml auto aktiviert ist, kann jeder, der auf dieses Repository pushen kann, die App ändern
Aliase, PHP-Einstellungen, Worker, Healthcheck und Backup-Profile. Darum geht es
Konfiguration als Code – Behandeln Sie den Schreibzugriff auf das Repo entsprechend.auth.json
Manage the 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 Zugangsdaten (z. B. API) zu speichern
Schlüssel, Feature-Flags oder jede JSON-Nutzlast), die Ihre Laravel-App zur Laufzeit lesen kann.
$ 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ähigkeitapps-auth, API
1.14+) – Composer/strukturiert 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 zushared_files in der Deployer-Konfiguration der App, sodass sie bei jedem symbolisch verknüpft ist
bereitstellen.
|
cipi auth edit <app> |
Öffnet shared/auth.json in $EDITOR (fällt zurück auf
nano). Validiert nach dem Schließen des Editors die JSON mit
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.jsonzumshared_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.
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 providers
Cipi ist einsatzbereit GitHub und GitLab aber es unterstützt alle anderer Git-Anbieter, der SSH-Bereitstellungsschlüssel unterstützt – keine Anbieterbindung.
Bei self-hosted- oder benutzerdefinierten Git-Servern 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:
# 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
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önlicher Zugangstoken, Cipi
fügt bei jeder Ausführung automatisch den SSH-Bereitstellungsschlüssel hinzu und erstellt webhook im Repository
cipi app create. Keine manuellen Schritte erforderlich.
Speichern Sie einen Token
# 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.
Automatic lifecycle
| Veranstaltung | Was Cipi automatisch macht |
|---|---|
app create |
Fügt den Bereitstellungsschlüssel hinzu und erstellt über API webhook im Repository. 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 einen persönlichen Zugangstoken im Wert von GitHub |
cipi git gitlab-token <token> |
Speichern Sie einen persönlichen Zugangstoken im Wert von GitLab |
cipi git gitlab-url <url> |
Legen Sie die Basis-URL für eine self-hosted GitLab-Instanz fest |
cipi git remove-github |
Entferne den gespeicherten 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 fällt Cipi auf die zurück Der manuelle Workflow erfolgt und die App-Erstellung verläuft normal.
So konfigurieren Sie den Bereitstellungsschlüssel und webhook manuell:
# 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 → Neu hinzufügen webhook. Legen Sie die Payload-URL fest und
Geheimnis der von gezeigten Werte
cipi deploy myapp --webhook
Anpassen des Bereitstellungsskripts
Die Bereitstellungskonfiguration für jede App wird gespeichert unter:
Diese Datei wird von Cipi während 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 generiertedeploy.php führt diese Aufgaben der Reihe nach aus:
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
Adding custom tasks
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:
// 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 einafter() oder before(). Das tust du nicht
Dies muss vermieden werden; Die Erweiterung der Pipeline ist genau das, wofür die Datei gedacht ist.
Cipi Installationen Node.js und npm auf dem Server während der Einrichtung. Stellen Sie sicher, dass sie verfügbar sind als der App-Benutzer:
$ ssh myapp@your-server-ip
myapp@server:~$ node -v && npm -v
Begehen package.json und package-lock.jsonzu 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:
// ── 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:
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):
add('shared_dirs', ['node_modules']);
Bearbeiten Sie die Datei auf dem Server als App-Benutzer und testen Sie sie dann mit cipi deploy myapp:
$ 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
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
// Seed only in specific environments
task('artisan:db:seed', function () {
run('{{bin/php}} {{release_path}}/artisan db:seed --force');
});
cipi app edit myapp --php=X oder cipi app edit myapp --branch=X. Sichern
Ihre Anpassungen oder bewahren Sie sie in einem Bereich 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 abzulegen
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:
// 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:
$ 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
~/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
With 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 AgentWeg. 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 (recommended) | CI/CD-Pipeline über SSH | |
|---|---|---|
| Auslöser | Git-Anbieter postet an /cipi/webhook auf Druck |
GitHub Aktionen / GitLab CI-Job läuft 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 – siehesicher 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 | Where to read more |
|---|---|---|
Einzelne Laravel-App, Push-to-Deploy auf main |
Webhook + Agent | Webhook setup |
| Nur bereitstellen, wenn die CI-Tests erfolgreich sind | Pipeline SSH (Produktion deaktivieren webhook) | 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 Parallelschaltung cipi deploy |
Multi-App-Bereitstellung |
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-Quittierung von langsamer Deployer Arbeit. Eine Bereitstellung kann mehrere Minuten dauern. Git-Anbieter überschreiten webhook HTTP Anrufe nach ca. 10 Sekunden. Cipi löst dieses Problem mit einer Flag-Datei und der Crontab des App-Benutzers.
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
Korrigieren Sie PHP Binär- und Dateiberechtigungen – der gleiche Kontext wie in einem Handbuch
cipi deploy myapp. Die webhook gehen niemals direkt an den Bereitsteller; es lässt nur das fallen
Trigger-Datei, die von der Crontab von Cipi bereits überwacht wird.
Voraussetzungen
| Requirement | 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 seincurrent vor dem webhook loslassen
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 installzuerst |
CIPI_WEBHOOK_TOKEN in shared/.env |
Auto-generated at 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:
$ 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:
$ 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 den webhook.Wenn du einen GitHub- oder GitLab-Token gespeichert hast, sind möglicherweise Cipi vorhanden
hat die webhook bereits erstellt app create – erkundigen Sie sich bei
cipi git status. Andernfalls rufen Sie die URL und das Geheimnis ab:
$ cipi deploy myapp --webhook
Fügen Sie die webhook in Ihrem Git-Anbieter hinzu:
| Anbieter | Payload URL | Secret field | 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):
CIPI_DEPLOY_BRANCH=main
Stellen Sie dies einshared/.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:
$ 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 webhook-Lieferung 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 auf dem Server konfiguriert ist, Cipi
Registriert den Bereitstellungsschlüssel understellt die webhook automatisch an 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 returns 404 | Agent noch nicht bereitgestellt | Lauf cipi deploy myapp nach dem Hinzufügen cipi/agent zucomposer.json
|
| Webhook gibt 403 / ungültige Signatur zurück | Geheimes Missverhältnis | Token erneut kopieren voncipi deploy myapp --webhook in die Providereinstellungen ein
|
| 200 OK, aber keine Bereitstellung | Branch filtered out | Überprüfen CIPI_DEPLOY_BRANCH entspricht dem gepushten Zweig |
| Bereitstellung hängt/Sperrfehler | Previous deploy interrupted | 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 |
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 webhook-Modell 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 conditional – 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 - Safe release — Snapshot der Datenbank und
shared/bis S3 vor Tauschen des Symlinks (sicher bereitstellen) - Sichtbarkeit des Teams – Posten Sie Erfolg/Misserfolg an Slack oder Telegram mit aktiviertem Rollback Fehler (Benachrichtigungen bereitstellen)
- Review apps — Erstellen oder aktualisieren Sie eine vollständige Cipi-App pro Funktionszweig (Vorschauumgebungen)
- Multi-app monorepo – bereitstellen
frontendundapiin 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 Gesundheitschecks und MCP verwenden.
SSH-Zugriff für CI
/root/.ssh/authorized_keys auf dem Server (bzw
cipiBenutzer, 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.
# 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.
# .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:
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.
# .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:
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:
# 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 Sie 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 hat seinen eigenen Auslöser – es kommt nie zu Konflikten, da es sich um unterschiedliche Cipi-App-Benutzer handelt.
Stellen Sie Benachrichtigungen bereit
Pipeline use case:Der webhook-Pfad 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 danach cipi deploy um Erfolg, Misserfolg und Automatik zu übertragen
Rollbacks zu Slack oder Telegram. Die beiden folgenden Beispiele 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.
# 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
Für GitLab CI verwendencurl direkt – kein Plugin erforderlich:
# .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.
# 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):
# .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
https://api.telegram.org/bot<TOKEN>/getUpdates und suche nach demchat.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-required
oder aktivieren cipi app edit <app> --predeploy-snapshot.
Pipeline use case: Bei einer webhook-Bereitstellung kann vor der Codefreigabe kein Sicherungsschritt ausgeführt werden –
Das Push-Ereignis wird sofort bereitgestellt. In einemCI/CD Rohrleitung, 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 Backup-Befehle, die zwei verschiedenen Sicherheitsstufen zugeordnet sind:
# 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.
cipi backup configure einmal auf dem Server zu
Verknüpfen Sie vorher Ihre S3-Zugangsdaten 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 auf S3 unter hochgeladen
Pfadcipi/<app>/<timestamp>/. Nach erfolgreichem Abschluss werden die temporären Dateien gelöscht
hochladen.
GitHub Aktionen – sicherer Bereitstellungsworkflow
# .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: test → backup → deploy. 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
# .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:
# 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
# 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/
.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 -fIn diesem Beispiel werden die fünf aktuellsten Snapshots beibehalten und ältere gelöscht.
Vorschauumgebungen (Bereitstellung pro Zweig)
Pipeline use case: Webhooks verweisen auf eine einzelne Produktions-URL – sie können keine erstellen
neue Cipi App pro Filiale. 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
Die Zweigstelle kann ihre 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:
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:
# 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)
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 SSL certificate — Erhalten Sie einmal ein Wildcard-Zertifikat über die Challenge DNS-01 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).
# .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).
# .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 "
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
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änen-Zertifikat mit Let's Encrypt Zertifikaten, das fehlschlägt (das
Die Domain hat keinen dedizierten DNS-Eintrag, nur den Platzhalter.