Vercel es la historia de despliegue para la que Next.js fue diseñado, configurada para mantenerse predecible
Preview URLs en cada PR, edge runtime donde paga, revalidación on-demand que tira la cache exactamente cuando el contenido cambia. Diseñamos el despliegue Vercel para que la factura sea algo sobre lo que planificas, no algo que descubres.
Por qué este stack
El despliegue es git push, no un ritual de CI
Un merge a main dispara una build, las builds emiten despliegues inmutables, el despliegue previo se mantiene vivo hasta que el nuevo está listo. Sin script de CI que mantener, sin imagen Docker que pushear, sin pánico de rollback a las 11 de la noche. Un despliegue malo es un click atrás al commit anterior.
Las preview URLs por PR son la feature decisiva para la review del cliente
Cada PR recibe una URL única con la app entera corriendo sobre una copia de la base de datos a la que la apuntes. Los stakeholders hacen click en un enlace y ven el cambio antes del merge, los diseñadores comparan lado a lado, el ida y vuelta que antes tomaba cinco emails pasa a ser un hilo de comentarios en la PR.
Edge runtime donde importa, Node donde toca
Auth checks, rewrites de locale, rate limits y personalización corren en edge para un first byte por debajo de los 100ms. Webhooks de Stripe, subidas de archivos y cualquier cosa que necesite la API Node completa se queda en el runtime Node. La elección es por ruta, no por app.
ISR con revalidación on-demand
Las páginas se renderizan una vez, se sirven desde la cache en el edge, se regeneran cuando el contenido cambia de verdad. Un webhook desde tu CMS llama a `revalidatePath` para la ruta exacta; la cache cae, la siguiente petición reconstruye. Sin rebuilds nocturnos vía cron, sin adivinanzas de invalidación de cache.
El coste es predecible cuando lo diseñas
La famosa factura de Vercel es lo que pasa cuando un equipo la trata como una caja mágica. Diseñamos el despliegue para que el coste viva en tres sitios (ejecución de funciones, ancho de banda, optimización de imágenes), con un umbral de alerta en cada uno. La factura pasa a ser una línea de presupuesto, no una sorpresa trimestral.
Qué construimos con esta tecnología
Setup del proyecto Vercel
Entornos (production, preview, development), env vars sincronizadas, configuración de dominios, redirects y headers de seguridad en `vercel.json`.
Workflow de preview URLs
Integración GitHub con branch protection, comentarios de preview en las PRs, resúmenes de despliegue publicados en Slack o Linear donde el equipo trabaja.
Configuración del edge runtime por ruta
Las rutas que pegan a APIs externas y leen bases de datos corren en Node; las que solo personalizan headers o hacen rewrites simples corren en edge.
ISR + revalidación on-demand
Caches con TTL largo en páginas de contenido, revalidación disparada por webhook conectada al CMS o a Supabase, `revalidatePath` y `revalidateTag` en Server Actions.
Optimización de imágenes bien conectada
`next/image` con `sizes` explícitos, `deviceSizes` e `imageSizes` ajustados, allowlist de patrones remotos, AVIF donde paga.
Edge Config para feature flags
Lecturas en sub-milisegundo de feature flags desde cualquier sitio de la app, sin servicio extra que gestionar, patrones de rollout gradual conectados.
Cron jobs
Tareas programadas definidas en `vercel.json` con handler deduplicante, timeouts por job, alertas en fallo.
Log Drains hacia tu stack de observability
Logs de funciones en stream hacia Datadog, Axiom o destino de elección, logging estructurado desde la aplicación emitiendo JSON, request IDs trazados de extremo a extremo.
Analytics + Speed Insights
Vercel Analytics para page views, Speed Insights para Core Web Vitals por ruta, ambos conectados a alertas cuando un release regresa.
Dominios custom, redirects, headers
Setup multi-dominio para multi-locale, redirects 301 desde las URLs viejas, headers CSP y HSTS configurados en `vercel.json`.
Monitoreo de costes + alertas
API de uso de Vercel consultada a diario, alertas por categoría (ejecución de funciones, ancho de banda, optimización de imágenes), informes de tendencia mensuales.
Migración desde Netlify, Heroku, AWS
Despliegues existentes mapeados a sus equivalentes en Vercel, env vars migradas, pipelines CI/CD simplificados, infraestructura vieja deprecada con plan de cutover.
Una config de despliegue Vercel-native con edge, ISR y revalidación en un solo proyecto
Un `vercel.json` definiendo headers, redirects y crons; un `next.config.ts` con estrategia de imágenes y cache; un route handler que corre en edge para auth; una Server Action que llama a `revalidatePath` cuando el contenido cambia. Cuatro archivos; cubre el despliegue en producción sin secretos.
La mayoría de guías "despliega en Vercel" se quedan en darle al botón. Vercel en producción es una config que diseñas. Los cuatro archivos de abajo son los que enviamos: un vercel.json con headers, redirects y crons; un next.config.ts con la estrategia de cache escrita en claro; una route en edge que corre auth en menos de 50ms de first byte; y una Server Action que revalida la ruta correcta cuando el contenido cambia.
1. La config vercel.json
La config de producción vive en source control. Headers, redirects, crons y rewrites son revisables, diff-eables y atados al commit que los introdujo.
// vercel.json
{
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Strict-Transport-Security",
"value": "max-age=63072000; includeSubDomains; preload"
},
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "Referrer-Policy",
"value": "strict-origin-when-cross-origin"
}
]
},
{
"source": "/_next/static/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
],
"redirects": [
{
"source": "/blog/old-slug",
"destination": "/blog/new-slug",
"permanent": true
}
],
"crons": [
{
"path": "/api/cron/reconcile-stripe",
"schedule": "0 3 * * *"
},
{
"path": "/api/cron/refresh-sitemap",
"schedule": "0 4 * * *"
}
]
}
2. La estrategia de cache de Next.js
El next.config.ts hace explícita la estrategia de cache: ISR para páginas de contenido, sin cache para rutas autenticadas, optimización de imágenes ajustada a los breakpoints reales que usa el design system.
// next.config.ts
import type { NextConfig } from 'next'
const config: NextConfig = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [360, 640, 768, 1024, 1280, 1536, 1920],
imageSizes: [16, 32, 48, 64, 96, 128, 256],
remotePatterns: [
{
protocol: 'https',
hostname: 'images.example.com',
pathname: '/uploads/**',
},
],
},
experimental: {
// Cache ISR de larga duración para el blog; flush on-demand vía webhook.
isrFlushToDisk: true,
},
async headers() {
return [
{
source: '/api/:path*',
headers: [{ key: 'Cache-Control', value: 'no-store' }],
},
]
},
}
export default config
3. La route en edge
El auth check corre en edge en milisegundos. La route lee la cookie de sesión, la valida contra una clave pública JWT (sin round-trip a la base de datos), y o reescribe a la aplicación o redirige a login.
// app/api/auth/check/route.ts
import { NextResponse } from 'next/server'
import { jwtVerify } from 'jose'
export const runtime = 'edge'
const PUBLIC_KEY = process.env.SESSION_PUBLIC_KEY!
export async function GET(request: Request) {
const cookie = request.headers.get('cookie')
const token = cookie?.match(/session=([^;]+)/)?.[1]
if (!token) return NextResponse.json({ ok: false }, { status: 401 })
try {
const { payload } = await jwtVerify(
token,
new TextEncoder().encode(PUBLIC_KEY),
)
return NextResponse.json({ ok: true, userId: payload.sub })
} catch {
return NextResponse.json({ ok: false }, { status: 401 })
}
}
4. Revalidación on-demand desde una Server Action
Cuando un editor de contenido publica un post, la Server Action escribe en la base de datos y llama a revalidatePath para la route exacta. La versión cacheada cae; la siguiente petición reconstruye y vuelve a cachear. Sin rebuild nocturno.
// app/admin/posts/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
import { createServerClient } from '@/lib/supabase/server'
export async function publishPost(postId: string): Promise<{ ok: true } | { ok: false; error: string }> {
const supabase = await createServerClient()
const { data, error } = await supabase
.from('posts')
.update({ status: 'published', published_at: new Date().toISOString() })
.eq('id', postId)
.select('slug, locale')
.single()
if (error) return { ok: false, error: error.message }
// Tira la cache para este post exacto y para el índice del locale.
revalidatePath(`/${data.locale}/blog/${data.slug}`)
revalidatePath(`/${data.locale}/blog`)
return { ok: true }
}
5. Qué te compra esto
El despliegue es un git push. La preview es una URL que aparece en la PR. El auth check responde en 30 milisegundos. La cache del blog se regenera en el momento en que un editor pulsa publish, no en un cron nocturno. La factura aterriza donde la esperas porque cada decisión de cache vive en source control donde puedes auditarla.
El Vercel que se gana mala reputación es el configurado por casualidad. El Vercel que se gana su sitio es el diseñado, con intención, tres o cuatro archivos cada vez.
Preguntas frecuentes
¿Vercel o Node self-hosted?
Vercel por defecto por la experiencia de despliegue, el edge runtime, los preview URLs y la feature parity con Next.js. Self-hosted sobre Node cuando el entorno de procurement exige on-premise, cuando los costes de egress dominarían, o cuando hay un equipo de ops interno con capacidad. El código aplicativo se mantiene portable; solo cambia el target de despliegue.
¿Cómo mantenéis la factura de Vercel predecible?
Tres puntos de disciplina. Ejecución de funciones: mantén los Server Components rápidos, evita trabajo pesado en el request path, empuja las tareas lentas a jobs en background. Ancho de banda: cachea las páginas con agresividad, optimiza imágenes, usa una CDN para los assets estáticos grandes. Optimización de imágenes: `sizes` explícitos en todas partes, AVIF donde paga, allowlist de patrones remotos estrecha.
¿El plan gratuito funciona para producción?
Para un side project y un SaaS pequeño, a menudo sí. Para cualquier cosa con tráfico real, el plan Pro se paga solo en plazas, ancho de banda y optimización de imágenes incluidas. Modelamos el coste en la fase de scoping basándonos en tráfico esperado, no en optimismo.
¿Y el ancho de banda a escala?
El pricing del ancho de banda de Vercel es directo; la trampa son los assets sin optimizar. Emparejamos Vercel con Cloudflare R2 para los archivos estáticos grandes, afinamos `next/image` correctamente, cacheamos todo lo cacheable. El ancho de banda pasa a ser una línea más pequeña que la ejecución de funciones en la mayoría de proyectos.
¿Cuán amplia es la red edge?
El edge de Vercel corre en varias regiones del mundo. Para la mayoría de proyectos es más que suficiente. Por razones específicas de compliance (data residency UE en cada petición) fijamos regiones explícitas y documentamos el trade-off.
¿Cuándo recurrís a infraestructura custom?
Cuando el workload es de verdad incompatible con serverless: jobs en background de larga duración, conexiones WebSocket persistentes que sobreviven a una función, uso pesado de memoria o GPU. En esos casos corremos el workload en un host dedicado y mantenemos la aplicación Next.js en Vercel.
¿Cuánto dura la migración desde Netlify o Heroku?
Una o dos semanas para una aplicación mediana. El cutover en sí es un día con prep DNS antes. El trabajo mayor es reescribir las pipelines CI/CD (a menudo más simples en Vercel), reorganizar el inventario de env vars y revisar redirects y headers. Lo hacemos como un único encargo a alcance fijo.
Cuéntanos sobre tu despliegue Vercel
Una llamada de scoping, un número concreto en la primera respuesta, sin teatro de agencia. Nuevos despliegues y migraciones desde Netlify, Heroku o AWS igual de bienvenidos.