Implementar y CI/CD
cipi deploy
Cipi usos Implementador para todas las implementaciones. Cada implementación es atómica: una nueva versión
El directorio está completamente preparado antes de la current El enlace simbólico se intercambia, por lo que el tráfico nunca se interrumpe.
interrumpido.
cipi deploy y cipi deploy --rollback
cancelar con un mensaje de actualización claro antes invocar Deployer cuando una aplicación todavía está fijada
a una versión anterior PHP: cámbiela con cipi app edit <app> --php=8.3 (o
superior) primero.Implementar canalización
Deployer and Composer run with the versión PHP configurada de la aplicación (por ej./usr/bin/php8.5), no el valor predeterminado del sistema. Esto se aplica a cipi deploy,
cipi deploy --rollback, activadores de implementación de crontab, cipi sync import despliega,
y el deploy / composer alias en el usuario de la aplicación .bashrc.
- Detener trabajadores en cola (
cipi worker stop) - Clonar repositorio en
releases/N/ - correr
composer install --no-dev(con la aplicación PHP) - Enlace
shared/.envyshared/storage/ - correr
artisan migrate --force - correr
artisan optimize - correr
artisan storage:link - Intercambiar
currentenlace simbólico atómicamente - Reiniciar trabajadores de la cola
- Pode las versiones antiguas (conserve las últimas 5)
$ 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)
Saber si una implementación funcionó (v5.1.0+)
Hasta 5.0.x, una implementación fallida podía pasar en silencio: la rama de falla, sus advertencias, la reversión
pista y el deploy_fail El correo electrónico era un código inalcanzable, porque una salida distinta de cero de
El implementador mató al cipi proceso en el acto. desde v5.1.0:
- Ambos caminos envían correos electrónicos sobre el éxito y el fracaso. — el CLI y el Git automático
webhook por igual. Los sujetos son visiblemente diferentes.
(
Cipi deploy succeeded: myapp release 55 …/Cipi deploy FAILED: myapp …), y el cuerpo nombra la sucursal, el número de versión, el hash de confirmación y el asunto, su autor y fecha, la duración, la versión anterior, la implementación ruta de registro y el veredicto de verificación de estado posterior a la implementación. cipi deploysale distinto de cero cuando falla la implementación, entonces CI y los webhooks pueden verlo. Lo mismo se aplica acipi deploy --rollback.- Se envía el correo electrónico de éxito. después verificación posterior a la implementación, por lo que nunca podrá anunciar una implementación exitosa mientras el sitio devuelve 500.
Leyendo el registro de implementación
/home/<app>/logs/deploy.log used to be raw Deployer output appended forever, which
made a deploy that failed overnight unreadable afterwards. Since v5.1.0 cada línea
tiene una marca de tiempo y cada ejecución está entre corchetes por un banner que nombra el desencadenante (CLI o webhook), el
rama, el lanzamiento y la duración.
$ cipi deploy myapp --log=100 # same as tailing /home/myapp/logs/deploy.log $ cipi deploy myapp --releases # release number, date, commit, subject
Los directorios de versiones siguen siendo numéricos (la reversión depende de ese orden), por lo que --releases
agrega el detalle humano en la parte superior en lugar de cambiar el nombre de nada.
desde v5.0, inscribirse instantáneas de base de datos previas a la implementación están disponibles a través de
--snapshot / --snapshot-required o una configuración de aplicación permanente; consulte
Instantáneas de base de datos previas a la implementación. Octane aplicaciones usan el
laravel-octane.php Plantilla de implementación (recargar/reiniciar Octane durante la implementación); habilitar nodo
construye concipi app edit <app> --node-build='…'.
cipi deploy myapp --unlock para eliminarlo antes de volver a implementarlo.Instantáneas de base de datos previas a la implementación
desde v5.0, cipi deploy <app> puede volcar la base de datos
antes se ejecuta el proceso de lanzamiento. Las instantáneas aterrizan debajo
/var/log/cipi/backups/ - el mismo camino utilizado por
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
que pasa
- Antes de que se inicie Deployer, Cipi realiza un volcado de base de datos en
/var/log/cipi/backups/. - con
--snapshot-required, un volcado fallido (o falta un motor de base de datos) bloques el despliegue. - con
--snapshotsolo, un volcado fallido imprime una advertencia y la implementación continúa.
Habilitar en cada implementación
Actívelo permanentemente para una aplicación para que cada implementación (CLI, webhook o canalización) tome una instantánea primero:
$ cipi app edit shop --predeploy-snapshot
cipi deploy --rollback restaura el anterior código liberación solamente.
lo hace no restaurar la base de datos. Si necesita recuperar el volcado previo a la implementación, utilice
cipi db restore.Para flujos de trabajo de canalización que también archivan shared/ a S3 antes del lanzamiento, consulte
Implementación segura: copia de seguridad antes del lanzamiento.
cipi app deploy-config
desde v5.0.3, administrar opciones duraderas de recetas de Deployer almacenadas enapps.json y se aplica regenerando deploy.php de la plantilla -
una alternativa segura a la edición de formato libre PHP.
$ 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
DESCANSO: GET|PUT /api/apps/{name}/deploy-config (capacidad
apps-deploy-config, API 1.14+ / Cipi 5.0.3+).
MCP: AppDeployConfigShow, AppDeployConfigUpdate.
cipi.yml — configuración que viaja con el código
Disponible desde v5.1.0. Una aplicación puede llevar un cipi.yml archivo en su
repositorio que describe el estado que espera: alias de dominio, versión PHP
y configuraciones, bases de datos adicionales, trabajadores de cola o Horizon, Reverb, el
planificador, es control de salud y su copia de seguridad
estrategia. El archivo se encuentra junto al código, por lo que se revisa la configuración del servidor.
versionado y enviado como todo lo demás.
Comandos
$ 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
El archivo se busca en current/cipi.yml, entonces current/cipi.yaml, entonces
shared/cipi.yml — anular con --file=<path>.
Empezar desde lo que ya tiene el servidor
No es necesario que lo escribas a mano. cipi yml generate <app> imprime la aplicación
configuración tal como está en el servidor: alias, versión PHP y configuración por aplicación, su extra
bases de datos, sus trabajadores de cola (leídos de Supervisor), el programador y los perfiles de respaldo que
posee: como un archivo listo para confirmar.
$ cipi yml generate myapp > cipi.yml # then commit it $ cipi yml plan myapp # reports nothing to do
Cron expresiones vuelven como las más amigables every: 30mformulario donde se mapean limpiamente,
Los valores se citan siempre que un escalar simple se malinterprete, y el resultado se pasa a través del
validador antes de imprimir. Los perfiles de copia de seguridad de todo el servidor y la propia base de datos de la aplicación están deliberadamente
excluidos, esos siguen siendo suyos.
el archivo
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.miaplicación.com" - "*.miaplicación.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: ["miaplicación", "miaplicación_*", "inquilino_*"] exclude_tables: ["*.trabajos", "*.telescopio_*"] 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 toma un nombre de aplicación opcional: cipi yml example myapp
- para que las bases de datos y los perfiles de marcador de posición lleguen dentro del espacio de nombres y la plantilla de esa aplicación
valida tal cual.Las implementaciones ignoran el archivo hasta que usted opte por participar
No sucede nada durante la implementación hasta que ejecutas cipi yml auto <app> on. Con esa suscripción
dado, cada exitoso implementar conciliaciones - de ambos cipi deployy
el Git webhook, este último a través de una regla sudoers de alcance limitado. Un lanzamiento que no llevacipi.yml es una operación silenciosa y un archivo que no supera la validación se informa por correo electrónico
(yml_fail) y nunca aplicado parcialmente. Una reconciliación exitosa enciende
yml_apply.
Por qué es seguro aceptar a través de Git
El archivo llega desde un repositorio, por lo que cualquiera que pueda confirmarlo controla su contenido. es por lo tanto cerrado por falla en todo:
- Sólo puede configurar una aplicación que ya existe: nunca cree, cambie el nombre o eliminar uno.
- Sus bases de datos deben tener nombre
<app>o<app>_*, y su perfiles de respaldo<app>o<app>-*. - Su URL de verificación de estado debe resolverse en uno de los dominios propios de la aplicación; de lo contrario, se podría realizar una confirmación. el sondeador de cinco minutos del servidor en una dirección interna y lee la respuesta de la alerta correos electrónicos.
- Las claves desconocidas son errores y ningún campo contiene un comando de shell o una ruta para incluir.
- El analizador implementa un subconjunto YAML deliberadamente pequeño y rechaza anclajes, alias, etiquetas, fusiones claves, escalares de bloques y asignaciones de flujo directamente.
yml auto está activado, cualquiera que pueda acceder a ese repositorio puede cambiar la configuración de la aplicación.
alias, configuración PHP, trabajadores, comprobación de estado y perfiles de copia de seguridad. Ese es el punto de
configuración como código: trate el acceso de escritura al repositorio en consecuencia.auth.json
Gestionar el auth.json archivo para una aplicación. Este archivo vive en
/home/<app>/shared/auth.json y tiene un enlace simbólico automático en cada versión mediante
Implementador: exactamente igual .env. Úselo para almacenar datos de credenciales estructurados (por ejemplo, API
claves, indicadores de funciones o cualquier carga útil JSON) que su aplicación Laravel pueda leer en tiempo de ejecución.
$ 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
DESCANSO: GET|POST|PUT|DELETE /api/apps/{name}/auth (capacidad apps-auth, API
1.14+) — Composer/estructurado JSON, distinto de HTTP Autenticación básica.
MCP: AppAuthJsonShow, AppAuthJsonCreate,
AppAuthJsonUpdate, AppAuthJsonDelete.
Detalles del comando
| Comando | Descripción |
|---|---|
cipi auth create <app> |
Crea shared/auth.json con la estructura inicial
{"users":[]}, establece permisos para 640 (propietario
app:app), y añade auth.json a
shared_files en la configuración del implementador de la aplicación para que tenga un enlace simbólico en cada
desplegar.
|
cipi auth edit <app> |
Abre shared/auth.json en $EDITOR (vuelve anano). Después de cerrar el editor, valida el JSON con
jqy advierte si el archivo tiene un formato incorrecto.
|
cipi auth show <app> |
Imprime el contenido de shared/auth.json formateado con
jq.
|
cipi auth delete <app> |
Pide confirmación y luego lo elimina. shared/auth.json y elimina el
auth.json entrada desde shared_filesen el implementador de la aplicación
configuración.
|
Integración del implementador
cipi auth create agrega automáticamente auth.json al
shared_files lista en /home/<app>/.deployer/deploy.php, y
cipi auth delete lo elimina. Esto significa que el archivo se trata exactamente como
.env: persiste en todas las versiones y nunca se sobrescribe con una implementación.
cipi auth La operación se registra mediante log_action para
auditabilidad. el AUTH La sección también aparece en la salida de
cipi help.
Proveedores de Git
Cipi está listo para trabajar con GitHub y GitLab pero soporta cualquier otro proveedor de Git que admita claves de implementación SSH, sin dependencia del proveedor.
Para servidores Git self-hosted o personalizados, debe confiar en la huella digital del host del servidor antes de
El implementador puede clonar a través de SSH. Utilice el--trust-host bandera para agregar la huella digital a la
usuario de la aplicación ~/.ssh/known_hosts automáticamente:
# 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 entrada al usuario de la aplicación ~/.ssh/config entonces
ese Deployer puede llegar al servidor sin ninguna configuración adicional.
configuración automática de git
Si guardas un GitHub o GitLab Token de acceso personal, Cipi
agrega automáticamente la clave de implementación SSH y crea el webhook en el repositorio cada vez que ejecutacipi app create. No se requieren pasos manuales.
guardar una ficha
# 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 permisos de token
Se necesitan tokens detallados (recomendados) administración y
Ganchos web establecer en leer y escribiren los repositorios de destino. Fichas clásicas
necesito el repo alcance.
GitLab permisos de token
el api el alcance es el mínimo requerido: GitLab no ofrece un alcance más granular
que cubre tanto las claves de implementación como los webhooks.
Ciclo de vida automático
| Evento | Qué hace Cipi automáticamente |
|---|---|
app create |
Agrega la clave de implementación + crea webhook en el repositorio a través de API. El resumen muestra "configurado automáticamente ✓" en lugar de instrucciones manuales. |
app edit --repository=... |
Elimina la clave de implementación + webhook del repositorio anterior y luego las agrega al nuevo. |
app delete |
Elimina la clave de implementación + webhook del repositorio antes de eliminar la aplicación. |
cipi git comandos
| Comando | Descripción |
|---|---|
cipi git status |
Mostrar el estado de conexión del proveedor y los detalles de integración por aplicación (implementar ID de clave, webhook identificación) |
cipi git github-token <token> |
Guarde un token de acceso personal GitHub |
cipi git gitlab-token <token> |
Guarde un token de acceso personal GitLab |
cipi git gitlab-url <url> |
Establecer la URL base para una instancia self-hosted GitLab |
cipi git remove-github |
Retire la ficha GitHub almacenada |
cipi git remove-gitlab |
Eliminar el token GitLab y la URL almacenados |
Configuración manual (alternativa)
La configuración automática se omite cuando no se configura ningún token, cuando falla la llamada API (permisos incorrectos, repositorio no encontrado, límite de velocidad), o cuando el repositorio está alojado en un proveedor que no sea GitHub o GitLab (por ejemplo, Gitea, Forgejo, Bitbucket). En todos estos casos Cipi vuelve a caer en el flujo de trabajo manual y la creación de la aplicación continúa normalmente.
Para configurar la clave de implementación y webhook manualmente:
# 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
Luego agréguelos en la configuración del repositorio de su proveedor:
- Implementar clave — GitHub: Configuración → Implementar claves → Agregar clave de implementación; GitLab: Configuración → Repositorio → Implementar claves
- Webhook — GitHub: Configuración → Webhooks → Agregar webhook;
GitLab: Configuración → Webhooks → Agregar nuevo webhook. Establezca la URL de carga útil y
secreto de los valores mostrados por
cipi deploy myapp --webhook
Personalización del script de implementación
La configuración de implementación para cada aplicación se almacena. en:
Este archivo es generado automáticamente por Cipi durante app create y se actualiza automáticamente cuando
cambie la versión PHP o implemente la rama a través de cipi app edit. Puedes editarlo para personalizarlo.
la canalización de implementación, pero debe comprender las implicaciones antes de hacerlo.
Canalización de implementación predeterminada
El autogenerado deploy.php ejecuta estas tareas en orden:
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
Agregar tareas personalizadas
Puede agregar tareas antes o después de cualquier paso. Para ver un ejemplo completo de compilación de frontend (npm install && npm run build), ver
Construyendo activos frontend abajo. Otros ejemplos comunes:
// 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');
Creación de activos frontend (npm / Vite)
no hay dedicado cipi Bandera CLI para compilaciones de frontend (p. ej. npm install && npm run build).
Personalización deploy.php es el enfoque apoyado y esperado - definir un
Implementador task() y engancharlo con after() o before(). tu no
es necesario evitar esto; extender la canalización es exactamente para lo que sirve el archivo.
Cipi instalaciones Node.js y npm en el servidor durante la instalación. Verifique que estén disponibles como el usuario de la aplicación:
$ ssh myapp@your-server-ip
myapp@server:~$ node -v && npm -v
comprometerse package.json y package-lock.json a su repositorio. Engancha la construcción
después deploy:shared entonces .env está vinculado (Vite lee
VITE_*variables a partir de ahí) y antes deploy:symlink entonces
Los activos compilados existen en la versión antes de que entre en funcionamiento.
Agregue el bloque a continuación en el abajo de
/home/myapp/.deployer/deploy.php, debajo de las definiciones de tareas generadas automáticamente por 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');
Esto equivale a npm install && npm run build en cada despliegue. Prefiero
npm ci en producción cuando package-lock.json está comprometido: es más rápido y
reproducible. uso npm install en cambio, solo si no bloquea las dependencias.
Si necesita pasos de instalación y compilación separados (por ejemplo, almacenar en caché node_modules en todas las versiones),
dividirlos en dos tareas:
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');
Para acelerar implementaciones posteriores, puede conservar las dependencias entre versiones agregandonode_modules a los directorios compartidos del implementador (opcional, solo si su proyecto admite
eso):
add('shared_dirs', ['node_modules']);
Edite el archivo en el servidor como usuario de la aplicación, luego pruebe con 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 en GitHub Acciones
o GitLab CI antes el paso de implementación SSH, por lo que el servidor solo recibe activos prediseñados.
Ver CI/CD canalizaciones: implementación de SSH.Ejecutar comandos artisan adicionales
// 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 o cipi app edit myapp --branch=X. hacer copia de seguridad
tus personalizaciones o mantenlas en una sección claramente separada de los bloques administrados por Cipi. un
El patrón seguro es colocar todas las tareas personalizadas al final del archivo después de la tarea predeterminada.
definición.
Deshabilitar un paso predeterminado
Para omitir una tarea (por ejemplo, si maneja las migraciones manualmente), coméntela o elimínela del
deploydefinición de tarea:
// 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', ]);
Probando tus cambios
Después de editar deploy.php, siempre haga una implementación de prueba antes de pasar a producción:
$ 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 o vía
cipi app logs myapp --type=deploy. Compruébelo primero cuando solucione un problema
desplegar.
Implementar y CI/CD: descripción general
Con Cipi, CI (construir y probar) y CD (lanzamiento a producción) puede
ser divididos o combinados. En última instancia, cada implementación se ejecuta de la misma manera. Implementador
tubería en el servidor - clonar, composer install, migraciones, intercambio de enlaces simbólicos,
reinicio del trabajador. lo que cambia es que desencadena ese oleoducto.
Cipi admite dos modelos de disparador. Para la mayoría de las aplicaciones Laravel, comience con webhook + Cipi Agentecamino. Pase a una tubería CI/CD completa cuando necesite puertas, copias de seguridad o orquestación de infraestructura que un simple gancho de empuje no puede expresar.
Dos formas de activar una implementación
| Webhook + Cipi Agente (recomendado) | CI/CD canalización a través de SSH | |
|---|---|---|
| gatillo | PUBLICACIONES del proveedor de Git para /cipi/webhook al empujar |
GitHub Acciones / GitLab Ejecuciones de trabajos de CI cipi deploy a través de SSH |
| Acceso al servidor desde CI | Ninguno: solo HTTPS para el dominio de tu aplicación | Clave SSH dedicada almacenada como secreto de CI |
| Pruebas previas a la implementación | Ejecutar localmente o en un trabajo de CI independiente; El despliegue aún se activa al presionar a menos que desactives el webhook | Nativo: el paso de implementación se ejecuta solo después needs: test (o equivalente) pases |
| Copia de seguridad antes del lanzamiento | Manual o cron en el servidor | Trabajo de canalización — ver despliegue seguro |
| Vista previa/revisión de aplicaciones | No compatible desde el primer momento | Pipeline crea Cipi aplicaciones por sucursal; consulte vista previa entornos |
| Complejidad de configuración | Bajo — composer require cipi/agent + uno webhook |
Medio: clave SSH, secretos, flujo de trabajo YAML |
¿Qué enfoque debo utilizar?
| Caso de uso | Enfoque recomendado | Dónde leer más |
|---|---|---|
Aplicación Laravel única, push-to-implementar en main |
Webhook + Agente | Webhook configuración |
| Implementar solo si se pasan las pruebas de CI | Pipeline SSH (deshabilitar producción webhook) | Implementación de canalización SSH |
| Copia de seguridad de archivos DB + antes de cada lanzamiento de producción | Tubería SSH | Implementación segura con respaldo |
| Alertas de Slack/Telegram sobre el resultado de la implementación | Tubería SSH | Implementar notificaciones |
| URL efímera por rama de función (revisar aplicaciones) | Tubería SSH | Vista previa de entornos |
| Implemente varias aplicaciones en un servidor desde un repositorio | Ya sea: webhook por aplicación o una canalización con paralelo cipi deploy |
Implementación de múltiples aplicaciones |
cipi deploy myapp --unlock si se deja una cerradura atascada.
Implementaciones automáticas: Cipi Agente y webhook
cipi-agente (cipi/agent) es un paquete Laravel que expone
POST /cipi/webhook dentro de su aplicación en ejecución. Cuando GitHub o GitLab envía un empujón
evento, el agente valida la firma de la carga útil, reconoce inmediatamente y pone en cola una implementación en
el servidor: no hay SSH del corredor CI, no sudo, no hay puertos de entrada abiertos más allá de HTTPS.
Cómo funciona el flujo webhook
El diseño separa acuse de recibo rápido HTTP de Implementador lento trabajo. Una implementación puede tardar varios minutos; Los proveedores de Git agotan el tiempo de espera de webhook HTTP llamadas después de ~10 segundos. Cipi resuelve esto con un archivo de bandera y el crontab del usuario de la aplicación.
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 │
El implementador siempre se ejecuta como aplicación usuario de Linux (por ej. myapp), con el
corregir PHP permisos binarios y de archivos: el mismo contexto que un manual
cipi deploy myapp. El webhook nunca paga directamente al Deployer; solo deja caer el
desencadenar un archivo que el crontab de Cipi ya supervisa.
Requisitos previos
| Requisito | ¿Por qué? |
|---|---|
| Cipi aplicación creada con el repositorio Git | La clave de implementación debe clonar el repositorio; consulte configuración automática de git |
| Al menos una implementación manual exitosa | El paquete del agente debe estar presente en el current soltar antes del webhook
la ruta existe |
composer require cipi/agent en el proyecto |
Registra el /cipi/webhook validación de ruta y firma |
| Webhook URL accesible a través de HTTPS | Los proveedores de Git requieren una URL pública; usar cipi ssl install primero |
CIPI_WEBHOOK_TOKEN en shared/.env |
Generado automáticamente en cipi app create; compartido en todas las versiones |
Configuración paso a paso
1. Cree la aplicación e impleméntela una vez manualmente. para que el servidor pueda clonar su repositorio:
$ cipi app create --user=myapp --domain=myapp.com \ --repository=git@github.com:you/myapp.git --branch=main --php=8.5 $ cipi deploy myapp
2. Instale el Agente Cipi en su proyecto Laravel localmente, confirme y presione:
$ 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. Configure el webhook.Si guardó un token GitHub o GitLab, es posible que Cipi tenga
ya creó el webhook durante app create - consultar con
cipi git status. De lo contrario, recupere la URL y el secreto:
$ cipi deploy myapp --webhook
Agrega el webhook en tu proveedor de Git:
| Proveedor | URL de carga útil | campo secreto | Eventos |
|---|---|---|---|
| GitHub | https://myapp.com/cipi/webhook |
Secreto → valor de --webhook |
Sólo el push evento |
| GitLab | https://myapp.com/cipi/webhook |
Token secreto → mismo valor | Eventos push |
4. Restrict to your deploy branch (recomendado para producción):
CIPI_DEPLOY_BRANCH=main
Establecer esto en shared/.env vía cipi app env myapp. Empujes a otras ramas
recibir un skipped respuesta y no se realizan ejecuciones de implementación.
5. Verificar. Empuje un pequeño compromiso para main y mire el registro de implementación:
$ cipi app logs myapp --type=deploy # or on the server as the app user: $ tail -f /home/myapp/logs/deploy.log
Aproximadamente un minuto después de la entrega webhook, debería aparecer una nueva versión de Deployer. Confirma el
compromiso en vivo con php artisan cipi:status o el salud
comprobar punto final.
configuración automática de git
cuando un GitHub o GitLab ficha está configurado en el servidor, Cipi
registra la clave de implementación y crea el webhook automáticamente en cada
cipi app create. El resumen de la aplicación muestra "configurado automáticamente ✓"en lugar de manual
instrucciones. Eventos del ciclo de vida (app edit --repository, app delete) mantener
claves y webhooks sincronizados.
Solución de problemas
| Síntoma | causa probable | Arreglar |
|---|---|---|
| Webhook devuelve 404 | Agente aún no implementado | correr cipi deploy myapp después de agregar cipi/agent a
composer.json
|
| Webhook devuelve 403 / firma no válida | discrepancia secreta | Vuelva a copiar el token de cipi deploy myapp --webhook en la configuración del proveedor
|
| 200 OK pero sin implementación | Rama filtrada | comprobar CIPI_DEPLOY_BRANCH coincide con la rama empujada |
| Implementación atascada/error de bloqueo | Despliegue anterior interrumpido | cipi deploy myapp --unlock luego vuelve a intentarlo |
| Implementar ejecuciones dos veces con una sola pulsación | Webhook + tubería ambas activas | Desactive un activador: consulte descripción general |
deploy herramienta—
usa lo mismo .deploy-trigger mecanismo. Ver Cipi Agente
para controles de estado, MCP y funciones de anonimización.CI/CD canalizaciones: implementación de SSH
Cuando el modelo webhook no sea suficiente, ejecute GitHub Acciones o GitLab
CI/CDtrabajos que SSH ingresan al servidor e invocan cipi deploy. Este es el
elección correcta siempre que se deba realizar la implementación condicional - controlado en pruebas, precedido por copias de seguridad,
seguido de notificaciones u orquestando nuevas aplicaciones de vista previa.
Cuando necesitas una tubería en lugar de un webhook
- Puerta de calidad - correr
php artisan test, análisis estático o frontend se construye antes de que cualquier código llegue a producción - Liberación segura - tomar una instantánea de la base de datos y
shared/a S3 antes intercambiando el enlace simbólico (despliegue seguro) - Visibilidad del equipo — publicar éxito/fallo en Slack o Telegram con la reversión activada fracaso (implementar notificaciones)
- Revisar aplicaciones — crear o actualizar una aplicación Cipi completa por rama de funciones (entornos de vista previa)
- Monorepo multiaplicación - desplegar
frontendyapien paralelo después de un único trabajo de prueba
Para estos flujos de trabajo, desactivar la producción webhook (o nunca crear uno) así que solo se implementan los desencadenadores de la canalización. Aún puedes usar Cipi Agent en la aplicación para controles de estado y MCP.
Acceso SSH para CI
/root/.ssh/authorized_keys en el servidor (o el
cipi usuario si lo prefieres sudo cipi deploy) y almacenar el
clave privada como un secreto de CI. Nunca reutilice las claves de implementación de Git o las claves SSH personales.
# 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
Tienda SERVER_HOST (IP del servidor o nombre de host) junto a SERVER_SSH_KEY en tu
secretos del repositorio (GitHub) o CI/CD variables (GitLab).
GitHub Acciones: probar y luego implementar
Agregue la clave privada como un secreto de repositorio llamado SERVER_SSH_KEY y la IP del servidor comoSERVER_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
Para revertir una falla, extienda el paso del guión:
script: |
sudo cipi deploy myapp || (sudo cipi deploy myapp --rollback && exit 1)
GitLab CI/CD
Agregue la clave privada como una variable CI/CD llamada SERVER_SSH_KEY (tipo: Archivo) y el servidor
propiedad intelectual como 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"
Con reversión en caso de falla:
script:
- ssh root@$SERVER_HOST "cipi deploy myapp || (cipi deploy myapp --rollback && exit 1)"
Implementación de múltiples aplicaciones
Si la misma canalización administra varias aplicaciones en el mismo servidor:
# 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
Patrones de canalización avanzados
Una vez que la implementación de SSH funcione, componga estas secciones en un único flujo de trabajo de producción:
| Patrón | Lo que añade el oleoducto | Guía |
|---|---|---|
| Notificaciones | Mensaje de Slack o Telegram sobre éxito, fracaso y reversión automática | Implementar notificaciones |
| Implementación segura | cipi db backup + cipi backup run antes
cipi deploy; revertir en caso de falla
|
Implementación segura con respaldo |
| Vista previa de entornos | Crear/actualizar/eliminar aplicaciones por rama Cipi con comodín DNS + SSL | Vista previa de entornos |
A typical mature setup uses the webhook para una aplicación de preparación (comentarios instantáneos sobre cada empujar) y un tubería para producción (pruebas → copia de seguridad → implementar → notificar). Cada aplicación tiene su propio desencadenante: nunca entran en conflicto porque se dirigen a diferentes usuarios de la aplicación Cipi.
Implementar notificaciones
Caso de uso de canalización: la ruta webhook se implementa silenciosamente: Git devuelve 200 y el equipo
se entera solo si miran los registros. con un canalización SSH, agregar
pasos de notificación después cipi deploy para transmitir éxito, fracaso y automático.
reversiones a Slack o Telegram. Los dos ejemplos siguientes funcionan con GitHub acciones y GitLab CI utilizando
solo llamadas HTTP estándar, sin dependencias de plataforma adicionales.
flojo
Agregue un paso final que se publique en Slack webhook independientemente del resultado de la implementación. uso
if: always() en GitHub Acciones para que la notificación se active tanto en caso de éxito como de fracaso.
Crear un entrante
Webhook en su espacio de trabajo de Slack y almacene la URL como
SLACK_WEBHOOK_URL en tus secretos de CI.
# 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
Para GitLab CI, utilice curl directamente, no se necesita complemento:
# .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
telegrama
Crea un bot de Telegram a través de @BotFather, obtenga el token del bot y busque su ID de chat/grupo.
Guárdalos como TELEGRAM_BOT_TOKEN y TELEGRAM_CHAT_ID en secretos de CI.
# 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 equivalente de CI (puro curl, sin dependencias adicionales):
# .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 y busca el
chat.id campo en la respuesta. Para chats privados, simplemente envíe un mensaje al bot primero.
Implementación segura: copia de seguridad antes del lanzamiento
Para un volcado integrado justo antes de que se ejecute Deployer (no se requiere etapa de canalización), use
--snapshot / --snapshot-required
o habilitar cipi app edit <app> --predeploy-snapshot.
Caso de uso de canalización: una implementación webhook no puede ejecutar un paso de copia de seguridad antes de publicar el código:
los disparos del evento push se despliegan inmediatamente. en un CI/CD tubería, agrega un
dedicado backup etapa que debe triunfar antes deploy comienza. un
El flujo de trabajo de nivel de producción siempre debe crear un punto de restauración. antesel nuevo código va
vivir. Cipi proporciona dos comandos de respaldo complementarios que se asignan a dos niveles de seguridad diferentes:
# 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
Usados juntos en una canalización, le brindan un punto de restauración local rápido y una copia fuera del servidor de la base de datos y todos los archivos cargados. La implementación solo comienza si ambas copias de seguridad se realizan correctamente.
cipi backup configure una vez en el servidor para
vincule sus credenciales S3 antes cipi backup run se puede utilizar.
cipi db backup Funciona sin ninguna configuración: siempre está disponible.
Qué hace cada comando internamente
cipi db backup <app> llamadas
mysqldump --single-transaction --routines --triggers y gzips la salida a
/var/log/cipi/backups/<app>_<timestamp>.sql.gz. El archivo permanece en el
servidor y nunca se elimina automáticamente; agregue un paso de limpieza o un cron si el espacio en disco es importante.
cipi backup run <app> hace dos cosas: vuelca la base de datos conmariadb-dump --single-transaction en un directorio temporal y archiva todo el
/home/<app>/shared/ carpeta (que contiene .env,
storage/y cualquier archivo subido por el usuario). Luego, ambos archivos se cargan en S3 bajo el
camino cipi/<app>/<timestamp>/. Los archivos temporales se eliminan después de una exitosa
subir.
GitHub Acciones: flujo de trabajo de implementación segura
# .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"
El gráfico de trabajos impone el orden: test → backup → deploy. si
cualquier trabajo falla, los siguientes se omiten. Si el paso de implementación falla, el
rollback El paso se activa automáticamente y restaura la versión anterior del Deployer.
GitLab CI/CD — canalización de implementación segura
# .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 y backup-s3 están en la misma etapa por lo que se ejecutan en paralelo si
tiene varios corredores, lo que reduce el tiempo total de canalización. Ambos deben tener éxito antes de que
deploy comienza la etapa.
Restaurar desde copia de seguridad local
Si necesita revertir la base de datos a la instantánea tomada justo antes de la implementación:
# 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
Restaurar desde la copia de seguridad S3
# 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 archivo a /var/log/cipi/backups/. En un calendario de despliegue muy ocupado,
agregue una limpieza cron o conserve solo los últimos N archivos:ls -t /var/log/cipi/backups/myapp_*.sql.gz | tail -n +6 | xargs rm -fEste ejemplo mantiene las 5 instantáneas más recientes y elimina las más antiguas.
Vista previa de entornos (implementación por sucursal)
Caso de uso de canalización: Los webhooks apuntan a una única URL de producción: no pueden generar una
nueva aplicación Cipi por sucursal. Los entornos de vista previa requieren una CI/CD
tubería que ingresa mediante SSH al servidor, calcula un nombre de aplicación determinista de la rama y
correcipi app create o cipi deploy respectivamente. Cada no producción
La sucursal puede obtener su propia URL activa: una aplicación Laravel completamente implementada con su propia base de datos, trabajadores y
HTTPS. Este patrón a veces se denomina "aplicaciones de revisión" o "entornos efímeros".
El formato URL utiliza tres barras separadas por guiones, por lo que cada entorno es legible por humanos y globalmente único:
https://develop-acmeco-3a1f9c2e.preview.domain.ltd https://release-1-2-3-acmeco-3a1f9c2e.preview.domain.ltd https://main-acmeco-3a1f9c2e.preview.domain.ltd
Cómo se generan los identificadores
Se derivan tres valores en tiempo de ejecución de la canalización:
# 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}"
Requisitos previos (configuración única del servidor)
Agrabar
*.preview.domain.ltd → <server-ip> en su proveedor DNS. Todos los subdominios
resolver automáticamente; no se necesitan cambios DNS por rama.2. Certificado comodín SSL — obtener un certificado comodín a través del desafío DNS-01 una vez e instalarlo en el servidor. Ver el Dominios comodín sección para obtener instrucciones. La ruta del certificado utilizado por los ejemplos de canalización a continuación es
/etc/letsencrypt/live/preview.domain.ltd/.3. Acceso al repositorio — los ejemplos de canalización utilizan una URL HTTPS con una dirección personal token de acceso (PAT) integrado, por lo que no se necesita configuración de clave de implementación SSH por aplicación. La ficha sólo necesita leer acceso al repositorio.
GitHub Acciones
Agregue estos secretos al repositorio: SERVER_HOST, SERVER_SSH_KEY,
DEPLOY_WILDCARD_DOMAIN (por ej. preview.domain.ltd), GH_PAT (un
PAT detallada con acceso de lectura al repositorio).
# .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
Añade estas CI/CD variables: SERVER_HOST, SERVER_SSH_KEY (tipo de archivo),
DEPLOY_WILDCARD_DOMAIN, GL_TOKEN (un token de acceso al proyecto/grupo con
repositorio_lectura alcance).
# .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 automáticamente cuando se envía una solicitud de fusión.
fusionados añadiendo un rules: bloque que comprueba
$CI_MERGE_REQUEST_EVENT_TYPE == "merge_train" o usando un dedicado
workflow: con if: $CI_PIPELINE_SOURCE == "merge_request_event".
Notas y límites
cipi app list periódicamente y elimine las vistas previas obsoletas.El parche nginx SSL no es idempotente — si el oleoducto corre
cipi app create dos veces (por ejemplo, debido a un reintento), el awk parche será
aplicado nuevamente. El hash asegura APP_NAME es determinista, por lo que
if cipi app show guard evita la doble creación en condiciones normales.evitar correr
cipi ssl installen una aplicación de vista previa: lo hará
sobrescriba la configuración del certificado comodín con un certificado Let's Encrypt por dominio que fallará (el
El dominio no tiene un registro DNS dedicado, solo el comodín).