Llevar una app de Next.js de Vercel a un VPS: lo que medimos y lo que se rompió
Tutorial · · 8 min de lectura
Vercel sigue siendo la forma más rápida de poner en marcha una app de Next.js. La pregunta llega después, cuando la factura crece con cada desarrollador que entra y con un uso que no puedes prever. La respuesta habitual es «pásala a un VPS», pero poca gente cuenta cómo es eso de verdad.
Así que lo probamos. Cogimos una app de Next.js 16.4 tal como la genera create-next-app y le añadimos lo que suele dar miedo: route handlers, proxy.ts (el antiguo middleware), next/image y variables de entorno. Después la desplegamos desde GitHub en un servidor de pruebas con 2 vCPU, con SecuryBlack, que es nuestro producto. Aquí van las cifras, las dos cosas que se rompieron y cómo las arreglamos.
Lo que cuesta, en octubre de 2026
Vercel Pro cuesta 20 $ al mes por desarrollador e incluye 20 $ de crédito de uso. Desde septiembre de 2026, los equipos Pro nuevos tienen la Flat Rate CDN: 1 millón de peticiones y 1 TB de transferencia incluidos y, por encima, tramos de 20 $ al mes (10 millones de peticiones) a 300 $ (150 millones). Las funciones se pagan por CPU activa (desde 0,128 $ la hora) y por invocaciones (desde 0,60 $ el millón) cuando pasas de lo incluido.
Un VPS cuesta lo mismo todos los meses. Hetzner ha subido precios dos veces en 2026. En Alemania y Finlandia, sin IVA:
| Servidor | vCPU | RAM | Precio/mes | Tráfico |
|---|---|---|---|---|
| CX23 (Cost-Optimized) | 2 | 4 GB | 5,99 € | 20 TB |
| CPX12 (Regular Performance) | 1 | 2 GB | 11,99 € | 20 TB |
| CPX22 (Regular Performance) | 2 | 4 GB | 19,99 € | 20 TB |
El CX23 es el que mejor sale, pero cuando lo miramos aparecía sin existencias. Otros proveedores están en rangos parecidos.
SecuryBlack es gratis durante la beta. Después, el plan Hobby sigue gratis para un servidor y Pro cuesta 12 € al mes. Si te registras durante la beta, Pro se te queda en 6 € al mes de por vida.
Conviene ser sincero aquí. Si trabajas solo y tu tráfico es modesto, Vercel Pro a 20 $ al mes es un precio justo y puede que no ahorres mucho. Mudarse compensa cuando sois varios desarrolladores, cuando hay mucho trabajo en el servidor (CPU de funciones) o cuando ya pagas una base de datos en otro sitio: en el VPS va en la misma máquina.
Paso 1: añade una ruta de comprobación
Antes de pasar el tráfico a una versión nueva, SecuryBlack comprueba que responde. Dale una ruta barata que no toque la base de datos:
// app/api/health/route.ts
export function GET() {
return Response.json({ ok: true });
}
Paso 2: conecta el servidor y GitHub
Crea un VPS con Ubuntu o Debian, añádelo en el panel y ejecuta el comando de instalación que te da. Después conecta GitHub y elige qué repositorios puede leer SecuryBlack.
Paso 3: crea la app
En Apps → Nueva app elige el repositorio y la rama, el puerto 3000 y la ruta de comprobación /api/health. Deja el build en Automático: si el repositorio no tiene Dockerfile, Railpack detecta Next.js y construye la imagen.
Paso 4: variables de entorno
Aquí está la primera trampa (más abajo, los detalles): activa Build en todas las variables que empiezan por NEXT_PUBLIC_. Los secretos, como la URL de la base de datos o las claves de API, se quedan solo para la ejecución.
Paso 5: dominio y HTTPS
Crea un registro A que apunte tu dominio a la IP del servidor y añade el dominio a la app. El certificado lo emite Let's Encrypt y se renueva solo. Puedes añadir más dominios y redirigir www.
Paso 6: haz push
A partir de aquí, cada push a la rama vuelve a desplegar. El webhook de GitHub arrancó nuestro build más o menos un segundo después del push.
Las cifras
| Medido | |
|---|---|
| Primer despliegue, build incluido | 110 s |
| Cada push posterior (caché de build) | 59 s |
| Build con 2 vCPU | 106 s, 57 s con caché |
| RAM de la app en marcha | 74 MB |
| RAM extra durante el build | unos 0,4 GB |
| Tamaño de la imagen (Railpack) | 1,23 GB |
El cambio de versión no corta el servicio: la versión anterior sigue respondiendo hasta que la nueva pasa la comprobación, y entonces se retira. La RAM del build sale de muestras de un minuto, así que el pico real puede ser algo mayor. Con 2 GB de RAM tienes margen para una app pequeña. Con 1 GB no lo probamos.
Lo que se rompió
1. Las variables NEXT_PUBLIC_ llegaban vacías al navegador
Next.js copia las variables NEXT_PUBLIC_* dentro del JavaScript al construir, no al arrancar la app. Si solo las defines para la ejecución, el navegador recibe undefined.
Arreglo: activa Build en esas variables. En Vercel no se nota, porque todas las variables están disponibles al construir y al ejecutar.
2. Un route handler devolvía null en un secreto
Esta ruta leía un secreto de ejecución y siempre respondía hasSecret: false:
export function GET() {
return Response.json({ hasSecret: Boolean(process.env.SECRET_GREETING) });
}
En Next.js 16, un route handler GET que no lee nada de la petición se prerenderiza durante el build. SecuryBlack no pasa las variables de ejecución al build, para que los secretos no acaben dentro de la imagen, así que la ruta se guardó sin el secreto.
Arreglo: dile a Next.js que la genere en cada petición:
import { connection } from "next/server";
export async function GET() {
// Render at request time, so runtime env vars are read on the server.
await connection();
return Response.json({ hasSecret: Boolean(process.env.SECRET_GREETING) });
}
Los server components que leen secretos tienen el mismo problema y el mismo arreglo.
Lo que funcionó sin cambios
proxy.ts: nuestra cabecera llegaba en todas las páginas.next/image: las imágenes salían redimensionadas y en WebP. En un VPS ese trabajo lo hace tu servidor, que guarda el resultado en su caché.- App Router y server components: nada que cambiar.
Lo que pierdes al dejar Vercel
- La CDN global. Tu app se sirve desde donde esté tu servidor. Si tus visitas están repartidas por el mundo, pon Cloudflare delante para cachear los ficheros estáticos.
- Las previews por pull request. SecuryBlack todavía no las tiene.
- El escalado automático. Un VPS tiene una capacidad fija. Vigila la CPU y la RAM, y pasa a un servidor mayor cuando lo necesites.
- Vercel Analytics. Puedes instalar Umami o Plausible desde una plantilla, en el mismo servidor.
Lo que asumes (y lo que cubrimos)
Con tu propio servidor, su seguridad y sus datos son cosa tuya. Por eso SecuryBlack no se queda en los despliegues:
- una auditoría de seguridad de SSH, el cortafuegos, los puertos y las actualizaciones, con el comando exacto para cada arreglo;
- copias cifradas de PostgreSQL y de carpetas, con restauración y un aviso si alguna deja de ejecutarse;
- monitorización de uptime y alertas por email, Telegram o webhook.
Conclusión
Llevar una app de Next.js de Vercel a un VPS nos llevó una tarde. Casi todo fue entender dos detalles: las variables NEXT_PUBLIC_ se fijan al construir, y las rutas que leen secretos tienen que generarse en cada petición. A cambio tienes una factura fija y tu app, su base de datos y sus datos en un servidor que controlas tú.
Despliega tu primera app: es gratis durante la beta, y Pro se te queda en 6 € al mes de por vida si te registras ahora.