Saltar al contenido principal

Deploy Gratis con GitHub Actions: Automatiza tu Web en Netlify y Vercel (2026)

Configura GitHub Actions para hacer deploy automático en Netlify o Vercel. CI/CD paso a paso: build, tests, preview deployments y deploy a producción. Gratis.

Fran Cobos 7 min de lectura 1285 palabras

Tabla de contenidos

Haces git push, te vas a tomar café, y cuando vuelves tu web ya está actualizada en producción. Sin tocar ningún panel, sin ejecutar comandos manuales, sin errores por olvidar un paso.

Eso es CI/CD con GitHub Actions. Y es gratis.

¿Por qué no solo usar el auto-deploy de Netlify/Vercel?

Netlify y Vercel ya hacen deploy automático cuando pusheas. Pero no ejecutan tus tests. Si tienes un error de TypeScript, un test roto o un linting que falla, se despliega igual.

Con GitHub Actions añades una capa de control:

git push → GitHub Actions → Tests ✅ → Build ✅ → Deploy a Netlify/Vercel

                          Tests ❌ → Deploy cancelado (no se rompe producción)

Opción 1: Deploy a Netlify con GitHub Actions

Paso 1: Obtener tokens de Netlify

  1. Ve a app.netlify.com/user/applications
  2. Genera un Personal Access Token — cópialo, solo se muestra una vez
  3. En tu sitio de Netlify, copia el Site ID (Site settings → General → Site ID)

Paso 2: Configurar secretos en GitHub

En tu repositorio → Settings → Secrets and variables → Actions:

  • NETLIFY_AUTH_TOKEN: tu Personal Access Token
  • NETLIFY_SITE_ID: el Site ID de tu sitio

Paso 3: El workflow

# .github/workflows/deploy.yml
name: Build & Deploy

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

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout código
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - name: Instalar dependencias
        run: npm ci

      - name: Lint
        run: npm run lint --if-present

      - name: Tests
        run: npm test --if-present

      - name: Build
        run: npm run build

      # Solo deploy en push a main (no en PRs)
      - name: Deploy a Netlify
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        uses: nwtgck/actions-netlify@v3
        with:
          publish-dir: './dist'
          production-deploy: true
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

      # Preview deploy en PRs
      - name: Preview Deploy
        if: github.event_name == 'pull_request'
        uses: nwtgck/actions-netlify@v3
        with:
          publish-dir: './dist'
          production-deploy: false
          alias: pr-${{ github.event.pull_request.number }}
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

¿Qué hace cada paso?

PasoQué haceSi falla…
CheckoutClona tu repoNo arranca nada
Setup NodeInstala Node 22 con caché de npmNo instala deps
npm ciInstala dependencias (exactas del lockfile)Error de deps
LintVerifica estilo de códigoCancela deploy
TestsEjecuta tests unitariosCancela deploy
BuildGenera la carpeta dist/Cancela deploy
DeploySube dist/ a NetlifySolo si todo pasó

Preview Deploys: Cuando abres un PR, GitHub Actions genera una URL temporal como pr-42--tu-sitio.netlify.app. Puedes revisar los cambios antes de mergear.

Opción 2: Deploy a Vercel con GitHub Actions

Paso 1: Obtener token de Vercel

# Instala Vercel CLI
npm i -g vercel

# Login y linkea tu proyecto
vercel login
vercel link

Esto genera .vercel/project.json con tu orgId y projectId.

Paso 2: Secretos en GitHub

  • VERCEL_TOKEN: desde vercel.com/account/tokens
  • VERCEL_ORG_ID: de .vercel/project.json
  • VERCEL_PROJECT_ID: de .vercel/project.json

Paso 3: El workflow

# .github/workflows/deploy-vercel.yml
name: Deploy to Vercel

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

env:
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'
      - run: npm ci
      - run: npm run lint --if-present
      - run: npm test --if-present

  deploy:
    needs: test  # Solo si los tests pasan
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install Vercel CLI
        run: npm i -g vercel

      # Producción (push a main)
      - name: Deploy Production
        if: github.event_name == 'push'
        run: vercel --prod --token=${{ secrets.VERCEL_TOKEN }}

      # Preview (PRs)
      - name: Deploy Preview
        if: github.event_name == 'pull_request'
        run: vercel --token=${{ secrets.VERCEL_TOKEN }}

Nota: separar test y deploy en dos jobs permite que se ejecuten en paralelo si no hay dependencia, y deja claro que el deploy necesita que los tests pasen primero (needs: test).

Ejemplo real: Portfolio + Blog (Vite + Astro)

Este es un workflow adaptado al setup que uso en este blog — un portfolio Vite + blog Astro con build combinado:

# .github/workflows/deploy.yml
name: Deploy Portfolio + Blog

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      # Portfolio (Vite)
      - name: Install portfolio deps
        run: npm ci

      - name: Build portfolio
        run: npx vite build

      # Blog (Astro)
      - name: Install blog deps
        working-directory: ./blog
        run: npm ci

      - name: Build blog
        working-directory: ./blog
        run: npm run build

      # Deploy combinado
      - name: Deploy to Netlify
        uses: nwtgck/actions-netlify@v3
        with:
          publish-dir: './dist'
          production-deploy: true
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

Optimización: Caché de dependencias

Si tu build tarda mucho, la mayor parte del tiempo es npm ci. Con caché de node_modules, los builds posteriores son mucho más rápidos:

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'  # Cachea node_modules basándose en package-lock.json

Si tienes un monorepo con múltiples package-lock.json:

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'
          cache-dependency-path: |
            package-lock.json
            blog/package-lock.json

Notificaciones: Saber si el deploy falló

Opción A: GitHub Status Checks

Ya viene incluido. En cada PR ves el estado del workflow: ✅ verde o ❌ rojo.

Opción B: Notificación por email

GitHub te notifica por email si un workflow falla (activado por defecto en Settings → Notifications).

Opción C: Discord webhook

      - name: Notificar a Discord
        if: failure()
        run: |
          curl -X POST ${{ secrets.DISCORD_WEBHOOK }} \
            -H "Content-Type: application/json" \
            -d '{"content": "❌ Deploy fallido en `${{ github.repository }}`\nCommit: ${{ github.sha }}\nAutor: ${{ github.actor }}"}'

Workflow avanzado: Matrix builds

Si quieres probar tu código en múltiples versiones de Node:

  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [20, 22]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

Esto ejecuta los tests en Node 20 y Node 22 en paralelo. Si falla en alguna versión, lo sabrás antes de deployar.

Errores comunes

”npm ci can only install with an existing package-lock.json”

# Asegúrate de tener package-lock.json commiteado
git add package-lock.json
git commit -m "add lockfile"
git push

El deploy funciona pero la web no se actualiza

Verifica que publish-dir apunte a la carpeta correcta:

# ❌ Incorrecto si tu build genera dist/
publish-dir: './build'

# ✅ Correcto
publish-dir: './dist'

Secretos no encontrados

Los secretos de un fork NO se comparten. Si alguien hace fork + PR, el workflow no tendrá acceso a tus secrets. Esto es intencional (seguridad).

¿Cuánto cuesta?

PlanRepos públicosRepos privados
GitHub FreeMinutos ilimitados2.000 min/mes
GitHub ProMinutos ilimitados3.000 min/mes

Un build típico de Vite + Astro tarda ~2 minutos. Con 2.000 minutos al mes puedes hacer 1.000 deploys/mes en repos privados. Para proyectos personales, es gratis de facto.

Resumen

1. Configura secretos en GitHub (tokens de Netlify/Vercel)
2. Crea .github/workflows/deploy.yml
3. El workflow: checkout → install → lint → test → build → deploy
4. Push a main → deploy a producción
5. PR → preview deploy con URL temporal
6. Si los tests fallan → deploy cancelado

Recursos relacionados

Fran Cobos

Fran Cobos

Desarrollador Full Stack especializado en IA aplicada, automatización y desarrollo web. Escribo sobre herramientas, tutoriales y casos reales para programadores.

¿Necesitas desarrollo a medida?

Apps web, IA, módulos ERP — cuéntame tu proyecto.