Deployer en PHP: deploy automatizado con rollback, tareas custom y configuración por entorno

Deployer es una herramienta de deploy para PHP escrita en PHP. Se instala globalmente o como dependencia de Composer y permite definir tareas de despliegue en un fichero deploy.php. Gestiona el sistema de releases y symlinks de forma automática, lo que hace que los rollbacks sean instantáneos.

Instalación

# Como dependencia de Composer (recomendado para proyectos)
composer require --dev deployer/deployer

# O como phar global
curl -LO https://deployer.org/deployer.phar
mv deployer.phar /usr/local/bin/dep
chmod +x /usr/local/bin/dep

Estructura de releases en el servidor

/var/www/mi-app/
??? current/          ? symlink al release activo
??? releases/
?   ??? 1/            (release anterior)
?   ??? 2/            (release actual, current apunta aquí)
??? shared/           ? ficheros compartidos entre releases
    ??? .env
    ??? storage/

Fichero deploy.php básico

<?php
namespace Deployer;

require 'recipe/common.php';

// Configuración del proyecto
set('application', 'mi-app');
set('repository', '[email protected]:mi-usuario/mi-app.git');
set('git_tty', false);
set('keep_releases', 5);  // cuántos releases guardar para rollback

// Ficheros y directorios compartidos entre releases
set('shared_files',  ['.env']);
set('shared_dirs',   ['storage/logs', 'storage/uploads']);
set('writable_dirs', ['storage/logs', 'storage/uploads', 'bootstrap/cache']);

// Definición de hosts
host('produccion')
    ->setHostname('tu-servidor.com')
    ->setRemoteUser('deploy')
    ->setIdentityFile('~/.ssh/id_rsa')
    ->set('deploy_path', '/var/www/mi-app')
    ->set('branch', 'main');

host('staging')
    ->setHostname('staging.tu-servidor.com')
    ->setRemoteUser('deploy')
    ->set('deploy_path', '/var/www/mi-app-staging')
    ->set('branch', 'develop');

// Tareas personalizadas
task('composer:install', function () {
    run('cd {{release_path}} && {{bin/composer}} install --no-dev --optimize-autoloader --no-interaction');
});

task('artisan:migrate', function () {
    run('cd {{release_path}} && php artisan migrate --force');
});

task('php-fpm:reload', function () {
    run('sudo systemctl reload php8.3-fpm');
});

// Pipeline de deploy: orden de ejecución
desc('Deploy de mi-app');
task('deploy', [
    'deploy:info',
    'deploy:setup',
    'deploy:lock',
    'deploy:release',
    'deploy:update_code',
    'deploy:shared',
    'deploy:writable',
    'composer:install',
    'artisan:migrate',
    'deploy:symlink',
    'php-fpm:reload',
    'deploy:unlock',
    'deploy:cleanup',
    'deploy:success',
]);

// En caso de fallo, desbloquear y hacer rollback
after('deploy:failed', 'deploy:unlock');

Ejecutar el deploy

# Deploy a producción
dep deploy produccion

# Deploy a staging
dep deploy staging

# Ver qué haría sin ejecutar nada
dep deploy produccion --dry-run

# Deploy con verbose (ver cada comando)
dep deploy produccion -vvv

Rollback en segundos

Deployer mantiene los últimos N releases (configurado con keep_releases). Un rollback solo cambia el symlink current al release anterior:

# Volver al release anterior
dep rollback produccion

# Ver los releases disponibles
dep releases produccion

Tareas custom con hooks

<?php
// Limpiar caché de OPcache tras desplegar
task('opcache:clear', function () {
    run('php -r "if (function_exists("opcache_reset")) { opcache_reset(); echo "OPcache limpiadan"; }"');
});
after('deploy:symlink', 'opcache:clear');

// Notificar a Slack tras deploy exitoso
task('notify:slack', function () {
    $host    = currentHost()->getAlias();
    $release = get('release_name');
    $webhook = getenv('SLACK_WEBHOOK');
    run("curl -s -X POST '$webhook' -d '{"text":"Deploy $release a $host completado"}'");
});
after('deploy:success', 'notify:slack');

// Crear enlace simbólico personalizado en shared
task('setup:env', function () {
    if (!test('[ -f {{deploy_path}}/shared/.env ]')) {
        run('cp {{release_path}}/.env.example {{deploy_path}}/shared/.env');
        writeln('??  Recuerda editar /shared/.env con las credenciales de producción');
    }
});
before('deploy:shared', 'setup:env');

Configuración por entorno

<?php
// Variables distintas por host
host('produccion')
    ->set('branch', 'main')
    ->set('deploy_path', '/var/www/prod')
    ->set('opcache_reset_file', '/var/www/prod/current/public/opcache_reset.php');

host('staging')
    ->set('branch', 'develop')
    ->set('deploy_path', '/var/www/staging')
    ->set('opcache_reset_file', '/var/www/staging/current/public/opcache_reset.php');

// Leer la variable en la tarea
task('opcache:http', function () {
    $url = 'https://' . currentHost()->getHostname() . '/opcache_reset.php';
    run("curl -s '$url'");
});

Ventajas sobre un deploy manual

  • Cero downtime: el symlink se cambia solo cuando todo está listo; las peticiones siguen sirviendo el release anterior hasta ese momento.
  • Rollback en segundos: volver al release anterior es cambiar un symlink, no redeployar.
  • Reproducible: el mismo fichero deploy.php produce siempre el mismo proceso.
  • Historial: cada release queda guardado con su timestamp para auditoría.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP