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.
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
- Ve a app.netlify.com/user/applications
- Genera un Personal Access Token — cópialo, solo se muestra una vez
- 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 TokenNETLIFY_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?
| Paso | Qué hace | Si falla… |
|---|---|---|
| Checkout | Clona tu repo | No arranca nada |
| Setup Node | Instala Node 22 con caché de npm | No instala deps |
| npm ci | Instala dependencias (exactas del lockfile) | Error de deps |
| Lint | Verifica estilo de código | Cancela deploy |
| Tests | Ejecuta tests unitarios | Cancela deploy |
| Build | Genera la carpeta dist/ | Cancela deploy |
| Deploy | Sube dist/ a Netlify | Solo 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/tokensVERCEL_ORG_ID: de.vercel/project.jsonVERCEL_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?
| Plan | Repos públicos | Repos privados |
|---|---|---|
| GitHub Free | Minutos ilimitados | 2.000 min/mes |
| GitHub Pro | Minutos ilimitados | 3.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
- Cómo hice mi portfolio con Vite y Tailwind — el proyecto que desplegamos con este workflow
- Docker para desarrolladores — containeriza tu app antes de deployar
- Comandos Git esenciales — domina Git antes de automatizar con Actions