Cómo configurar un pipeline de despliegue automático con GitHub Actions

Deploy & DevOps · Por · Publicado el · Actualizado el · 8 min de lectura

Dos personas en una oficina revisando documentos sobre una mesa con un portátil
Revisión de planes y coordinación en equipo — metáfora para configurar pipelines de despliegue Foto: ANTONI SHKRABA production / Pexels

Este tutorial muestra, paso a paso, cómo configurar un pipeline de despliegue automático con GitHub Actions. Crearás un workflow YAML que ejecuta pruebas, hace build con Docker, publica la imagen en GHCR y realiza el despliegue por SSH con Docker Compose. El ejemplo es genérico y puede adaptarse a distintas stacks.

Requisitos previos

Necesitas un repositorio en GitHub y permiso para crear workflows. En el servidor de destino, ten instalados Docker y Docker Compose, acceso por SSH y puertos abiertos en el firewall. Crea un endpoint de healthcheck en tu aplicación. Define un dominio y apunta el DNS al servidor si vas a exponerla en la web.

Si tu proyecto aún no tiene pruebas automáticas, empieza por algo simple. Si usas código PHP heredado, revisa cómo iniciar pruebas gradualmente en código PHP legado.

  • Repositorio en GitHub con rama principal (p. ej.: main)
  • Servidor Linux con Docker y Docker Compose
  • Acceso SSH con clave privada
  • Endpoint /health o similar
  • Dominio configurado (opcional)

1) Crear el workflow básico

Comenzaremos con un workflow mínimo que se ejecuta en push y pull request a la rama principal. Hará checkout del código y preparará el entorno del pipeline. A partir de este punto lo iremos ampliando para añadir pruebas, build y deploy.

# .github/workflows/deploy.yml
name: CI/CD

on:
  push:
    branches: ["main"]
  pull_request:
    branches: ["main"]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

2) Cachear dependencias y ejecutar pruebas

Como ejemplo usaremos Node.js. Adáptalo a tu stack. El cache acelera las instalaciones. Ejecuta pruebas en cada push y PR para evitar regresiones. Si todavía no tienes tests, valida al menos un lint o un build rápido.

Para proyectos PHP/Laravel, hay una guía específica sobre GitHub Actions para PHP/Laravel.

# Adicione ao job build
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - name: Instalar deps
        run: npm ci
      - name: Testes
        run: npm test --if-present

3) Build de la imagen Docker y push al GHCR

Empaquetar la aplicación en Docker estandariza el despliegue. Publicaremos en GitHub Container Registry (GHCR) usando el GITHUB_TOKEN. Así evitas crear registries externos.

Si estás decidiendo entre contenedores y máquinas virtuales para producción, compara pros y contras en Docker vs máquinas virtuales.

  • Tags inmutables (SHA) facilitan trazabilidad y rollback
  • Define permissions -> packages: write para usar GHCR
# No mesmo arquivo, após o job build
  docker_image:
    needs: build
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    env:
      IMAGE_NAME: ghcr.io/${{ github.repository }}
    outputs:
      image_tag: ${{ steps.meta.outputs.tag }}
    steps:
      - uses: actions/checkout@v4
      - name: Definir tag da imagem
        id: meta
        run: echo "tag=${GITHUB_SHA}" >> $GITHUB_OUTPUT
      - name: Login no GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: Build e push
        uses: docker/build-push-action@v5
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: |
            ${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.tag }}
            ${{ env.IMAGE_NAME }}:main

4) Preparar el servidor con Docker Compose

En el servidor usaremos Docker Compose para orquestar la aplicación. El archivo abajo referencia la imagen del GHCR y usa las variables REPO_IMAGE e IMAGE_TAG, que enviaremos en el deploy.

Atención: ejecutar Compose con la nueva imagen reiniciará el servicio. Usa ventanas de mantenimiento o técnicas blue/green para evitar indisponibilidad.

# docker-compose.yml (no servidor)
version: '3.8'
services:
  app:
    image: ghcr.io/${REPO_IMAGE}:${IMAGE_TAG}
    env_file:
      - .env
    ports:
      - "80:3000"
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 3s
      retries: 5

# Mantenha um arquivo .env com as variáveis de runtime da sua app.

5) Deploy por SSH usando Docker Compose

El job de deploy se conecta por SSH, envía las variables de versión y ejecuta Docker Compose en el servidor. También realiza login en GHCR para poder tirar la imagen privada.

Almacena claves y tokens como secretos en el repositorio. Nunca comites secretos en Git.

  • Segredos necessários: SSH_HOST, SSH_USER, SSH_KEY, GHCR_USER, GHCR_TOKEN (read:packages)
  deploy:
    needs: docker_image
    runs-on: ubuntu-latest
    environment:
      name: production
    steps:
      - uses: actions/checkout@v4
      - name: Preparar chave SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
      - name: Enviar vars de versión
        env:
          IMAGE_TAG: ${{ needs.docker_image.outputs.image_tag }}
        run: |
          printf "REPO_IMAGE=%s\nIMAGE_TAG=%s\n" "${{ github.repository }}" "$IMAGE_TAG" > .env.deploy
          scp -i ~/.ssh/id_rsa .env.deploy ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:/opt/app/.env
      - name: Deploy remoto
        env:
          GHCR_USER: ${{ secrets.GHCR_USER }}
          GHCR_TOKEN: ${{ secrets.GHCR_TOKEN }}
        run: |
          ssh -i ~/.ssh/id_rsa ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
            "echo $GHCR_TOKEN | docker login ghcr.io -u $GHCR_USER --password-stdin && \
             cd /opt/app && docker compose pull && docker compose up -d && docker image prune -f"

6) Secretos, entornos y aprobaciones

Usa GitHub Environments para aislar secretos por entorno y exigir aprobación humana antes del deploy a producción. Define los secretos en el environment correspondiente y marca revisores obligatorios. En el YAML solo referencia el nombre del entorno. La aprobación se gestiona fuera del YAML, en la configuración del repositorio.

  • Crea environments: staging y production
  • Almacena secretos por entorno
  • Exige aprobación para production
  • Configura notificaciones (Slack, e‑mail) si es necesario
  deploy:
    needs: docker_image
    runs-on: ubuntu-latest
    environment:
      name: production
      url: https://sua-app.com
    steps:
      - uses: actions/checkout@v4
      # ... (restante igual ao passo anterior)

7) Cómo comprobar que funcionó

Valida el healthcheck y verifica los contenedores en ejecución. Confirma la versión por el tag/commit expuesto en la aplicación, si lo tienes. Automatiza una comprobación simple al final del deploy para fallar pronto en caso de error.

  • Healthcheck HTTP 200
  • Contenedor saludable en Docker
  • Logs sin errores críticos
# Do runner ou da sua máquina
curl -fsS https://sua-app.com/health && echo "OK"

# No servidor remoto
ssh $USER@$HOST "docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}' && docker compose ps && docker compose logs --tail=50"

8) Problemas comunes y soluciones

Aquí algunos errores frecuentes y cómo resolverlos. Evita cambios manuales en el servidor que no estén descritos como código: eso complica la reproducibilidad y el rollback. Para migraciones de base de datos, opta por prácticas seguras para evitar indisponibilidad.

  • Host key verification failed: ejecuta ssh-keyscan para poblar known_hosts.
  • Permissão negada no GHCR: confirma GHCR_USER y GHCR_TOKEN con el scope read:packages.
  • docker compose: comando ausente? Instala el plugin/CLI de Compose en el servidor.
  • Timeout en el healthcheck: revisa puertos y firewall; valida la ruta /health.
  • Rollback: ejecuta docker compose up -d con la tag anterior (SHA anterior).
  • Migraciones sin downtime: revisa versionado de bases de datos y migraciones sin downtime con CDC.

9) Próximos pasos y mejoras

Puedes añadir matrices de versiones, análisis estático y escaneo de vulnerabilidades. Para pipelines más grandes, audita tu proceso y aborda cuellos de botella, colas largas y puntos únicos de fallo. Cuando el despliegue sea crítico para el negocio, invierte en observabilidad y revisiones periódicas del pipeline.

  • Matrix de versiones (Node, Python, PHP)
  • Builds paralelos por servicio (monorepo)
  • Escaneo SCA/Container
  • Despliegues blue/green o canary
  • Observabilidad y alertas
  • Auditoría de procesos de deploy

Preguntas frecuentes

¿Necesito usar Docker para hacer deploy con GitHub Actions?

No. Puedes usar rsync para artefactos (build estático) o publicar paquetes e instalarlos en el servidor. Docker estandariza el runtime y facilita el rollback, pero no es obligatorio. Adapta el job de deploy a tu proceso actual (systemd, PM2, etc.).

¿Cómo hago un rollback rápido si la versión dio problema?

Usa tags inmutables con SHA. En el servidor, ejecuta Docker Compose fijando la tag anterior y aplica “docker compose up -d”. Mantén un historial corto de imágenes locales para acelerar el retorno y supervisa el healthcheck tras la reversión.

¿Cómo protejo secretos en el pipeline?

Almacena claves y tokens en los Secrets del repositorio o del Environment; nunca en el código. Restringe permisos, rota claves y utiliza entornos con aprobación para producción. Limita logs para no exponer valores sensibles durante el pipeline.

¿Puedo tener múltiples entornos (staging y producción)?

Sí. Crea dos environments y dos jobs de deploy o un único job parametrizado por un input manual (workflow_dispatch). Almacena secretos por entorno y permite el despliegue a producción solo tras la aprobación y la verificación en staging.

¿Y las migraciones de base de datos en el deploy?

Planifica migraciones con compatibilidad hacia atrás. Aplica primero cambios que no rompan la versión actual. Ejecuta migraciones en el pipeline con pasos idempotentes, monitorea el impacto y, cuando sea necesario, adopta estrategias de zero‑downtime con CDC.

¿Quieres ir más allá de lo básico del pipeline?

Si tu despliegue requiere estrategias como blue/green, canary, migraciones sensibles u observabilidad, evoluciona la arquitectura y el proceso con revisiones técnicas y automaciones a medida.

Discutir mejoras de CI/CD

Ver también