Escapar de Vercel: guía completa de autoalojamiento de Next.js con Docker

A finales del mes pasado, como siempre, abrí la página de facturación de Vercel. $47,32.
Hice un cálculo rápido: el tráfico del blog solo subió un 20% este mes, ¿cómo puede haberse duplicado la factura? Al abrir el desglose, el problema estaba en las invocaciones de serverless functions: una ruta API sin caché bien hecha se ejecutaba tres veces en cada refresco de página.
La experiencia de desarrollo en Vercel es muy fluida: git push y despliegue automático, red edge global, funciones listas para usar. Pero cuando el tráfico sube un poco, la factura se dispara. El plan Pro de $20 es solo la entrada; lo caro son los cargos por uso.
En ese momento pensé: toca sacar este proyecto de ahí.
Este artículo recoge el proceso completo de migrar mi proyecto Next.js de Vercel a autoalojamiento con Docker. Trampas, documentación consultada y configuraciones probadas, todo aquí. Si estás pensando en autoalojar o ya lo intentaste y te encuentras con cosas raras (404 en estáticos, streaming que no funciona), espero que te sirva.
¿Por qué escapar de Vercel?
Aclaremos algo: no voy a hablar mal de Vercel. En muchos escenarios sigue siendo la mejor opción — sobre todo proyectos enterprise, red edge global o equipos sin ops. Pero para proyectos personales y equipos pequeños, el coste duele.
La lógica de precios de Vercel
El plan gratuito parece generoso: 100 GB de ancho de banda, 1 millón de Edge Requests. El problema es que un proyecto con algo de tráfico se queda corto. Al subir a Pro ($20/mes), descubres que es solo la entrada:
- Invocaciones Serverless Function: cobro por uso por encima de 1 millón
- Tiempo de ejecución edge: cargo extra por encima de 1 millón GB-s
- Optimización de imágenes: cobro por uso por encima de 5000
- Ancho de banda: cobro por GB por encima de 1 TB
Lo peor: es difícil prever el uso. Una ruta API mal cacheada o una página bombardeada por crawlers dispara la factura.
¿Cuánto se ahorra con autoalojamiento?
Hice números. Mi proyecto en Vercel costaba unos $35-50/mes según tráfico. Tras migrar a un servidor DigitalOcean de $12/mes:
- Servidor: $12/mes (2 vCPU, 4 GB RAM; suficiente para dos o tres apps Next.js)
- Cloudflare CDN: gratis (ya lo usaba)
- Almacenamiento extra: $0 (disco local suficiente)
Ahorro de $25-40/mes, $300-500 al año. Y lo clave: el coste es fijo, sin picos por tráfico.
¿Cuándo conviene autoalojar?
No todo el mundo debería autoalojar. Yo lo consideraría si:
- ✅ Tienes base en Linux/Docker
- ✅ Tráfico relativamente estable; no necesitas red edge global
- ✅ Aceptas un despliegue manual de 5-10 minutos
- ✅ Presupuesto ajustado (proyecto personal, startup temprana)
En cambio, quédate en Vercel si:
- ❌ El equipo no tiene ops ni quiere aprender
- ❌ Tráfico muy variable; necesitas autoescalado
- ❌ Necesitas Analytics, Edge Config u otras funciones propias
- ❌ Presupuesto holgado; la velocidad de desarrollo pesa más
Piénsalo bien antes de moverte; no merece la pena ahorrar a costa de complicarte la vida.
Configuración clave del despliegue Next.js con Docker
Vamos al grano. El despliegue Docker de Next.js tiene tres puntos clave; domínalos y evitarás la mayoría de problemas.
1. Modo de salida Standalone
Es el paso más importante. Por defecto, next build genera muchos archivos, incluido node_modules completo. En Docker es enorme y arranca lento.
Añade esta línea en next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
}
module.exports = nextConfig
Tras npm run build verás el directorio .next/standalone. Contiene:
server.js: script de arranquenode_modulesreducido: solo paquetes de runtime- Código de la aplicación
Punto clave: standalone no copia automáticamente public ni .next/static. Hay que copiarlos manualmente al directorio standalone; si no, todos los estáticos dan 404. Me costó dos días encontrar esta trampa.
2. Dockerfile multietapa
Aquí va el Dockerfile que uso yo, con comentarios claros:
# ============ Fase 1: instalación de dependencias ============
FROM node:20-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Solo manifiestos de dependencias, para caché Docker
COPY package.json package-lock.json ./
RUN npm ci
# ============ Fase 2: build de la aplicación ============
FROM node:20-alpine AS builder
WORKDIR /app
# Copiar dependencias y código
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Variables de entorno de build (si hacen falta)
ENV NEXT_TELEMETRY_DISABLED=1
# Build
RUN npm run build
# ============ Fase 3: ejecución en producción ============
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
# Usuario no root (buena práctica de seguridad)
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copiar public (recursos estáticos)
COPY --from=builder /app/public ./public
# Copiar salida standalone
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
# Copiar static (CSS/JS compilados)
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# Comando de arranque
CMD ["node", "server.js"]
Explicación:
- Build en tres fases: dependencias, build y runtime separados; la imagen final solo lleva lo necesario, de ~1,5 GB a ~200 MB
COPY --from=builder /app/public: no lo olvides; sin esto favicon, robots.txt, etc. no carganCOPY ./.next/static: aún más crítico; sin esto todo JS/CSS da 404- Usuario no root: buena práctica; no ejecutes la app como root en producción
3. La trampa de las variables de entorno
Yo también caí aquí. En Next.js hay dos tipos:
- Variables de build: prefijo
NEXT_PUBLIC_, se compilan en el código - Variables de runtime: uso en servidor, p. ej. URL de base de datos
En modo Standalone, runtimeConfig no funciona. Lo recomendado oficialmente es App Router:
// app/api/example/route.ts
export async function GET() {
// Leer directamente desde process.env
const dbUrl = process.env.DATABASE_URL
// ...
}
Pasar variables al ejecutar Docker:
docker run -p 3000:3000 \
-e DATABASE_URL="postgres://..." \
-e API_KEY="xxx" \
your-image-name
O con docker-compose.yml:
version: '3.8'
services:
nextjs:
image: your-image-name
ports:
- "3000:3000"
environment:
DATABASE_URL: "postgres://..."
API_KEY: "xxx"
restart: unless-stopped
Nota: las variables NEXT_PUBLIC_ deben definirse en build; no se cambian en runtime. Para configuración dinámica en runtime, usa solo variables de servidor.
Puntos clave del proxy inverso
Puedes exponer el contenedor Next.js directamente a internet, pero no lo hagas. Una app Node.js a pelo no aguanta mucho ante peticiones maliciosas y slowloris. El proxy inverso no es opcional.
¿Por qué hace falta un proxy inverso?
- Seguridad: filtra peticiones maliciosas, rate limiting, anti-DDoS
- HTTPS: gestión centralizada de certificados SSL
- Varias apps: un servidor, varios proyectos por dominio/ruta
- Caché de estáticos: alivia la carga del servidor de aplicación
Yo uso Nginx, estable y fiable. Si quieres algo más simple, Caddy también vale (HTTPS automático, config más legible).
Ejemplo de configuración Nginx
server {
listen 80;
server_name yourdomain.com;
# Redirigir a HTTPS (si tienes SSL)
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name yourdomain.com;
# Certificados SSL (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
# Proxy inverso al contenedor Next.js
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
# Cabeceras necesarias
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Clave: desactivar buffering para streaming
proxy_buffering off;
proxy_cache off;
proxy_set_header X-Accel-Buffering no;
# WebSocket (si lo necesitas)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Caché de estáticos (opcional pero recomendado)
location /_next/static/ {
proxy_pass http://localhost:3000;
proxy_cache_valid 200 60m;
add_header Cache-Control "public, max-age=3600, immutable";
}
}
Tres configuraciones clave:
proxy_buffering off: sin buffering, el streaming no se bloqueaX-Accel-Buffering: no: indica a Nginx que no bufferice el cuerpo de la respuesta- WebSocket: con Socket.io o tiempo real, la cabecera
Upgradees obligatoria
Configuración simplificada con Caddy
Si Nginx te parece pesado, prueba Caddy:
yourdomain.com {
reverse_proxy localhost:3000 {
# Caddy no bufferiza por defecto; no hace falta config extra
}
}
Eso es todo. Caddy pide y renueva certificados Let’s Encrypt solo; la config es así de corta.
Integración con Docker Compose
Containerizar también Nginx simplifica la gestión:
version: '3.8'
services:
nextjs:
build: .
restart: unless-stopped
environment:
DATABASE_URL: "postgres://..."
# No exponer al host; solo nginx accede
expose:
- "3000"
networks:
- app-network
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./certs:/etc/letsencrypt
depends_on:
- nextjs
networks:
- app-network
networks:
app-network:
driver: bridge
El servicio nextjs usa expose en lugar de ports: solo contenedores de la misma red acceden; más seguro.
Solución cuando el streaming deja de funcionar
Este problema me tuvo un día entero. En local el chat con IA iba perfecto; en Docker el streaming murió — o esperabas mucho y salía todo de golpe, o se quedaba colgado.
Síntomas
Típicamente:
- Streaming de OpenAI/Anthropic que no responde en tiempo real
- Server-Sent Events (SSE) sin push inmediato
- La página tarda mucho y refresca de golpe, sin efecto letra a letra
En local con npm run dev todo bien; en producción, fallo.
Causa raíz
Dos sitios suelen provocarlo:
- Buffer del proxy inverso: Nginx bufferiza el cuerpo por defecto y envía al cliente cuando está completo
- Runtime de Next.js: rutas API sin Edge Runtime a veces no soportan streaming
Solución 1: configuración Nginx
Las tres líneas del capítulo de proxy inverso, otra vez:
proxy_buffering off;
proxy_cache off;
proxy_set_header X-Accel-Buffering no;
Van dentro del bloque location /. Tras cambiar, reinicia Nginx:
nginx -t # probar sintaxis
nginx -s reload # recargar
Solución 2: usar Edge Runtime
Si la ruta API hace streaming (p. ej. chat IA), añade al inicio del archivo:
// app/api/chat/route.ts
export const runtime = 'edge'
export async function POST(req: Request) {
const stream = new ReadableStream({
async start(controller) {
// Tu lógica de streaming
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [...],
stream: true,
})
for await (const chunk of response) {
controller.enqueue(chunk.choices[0]?.delta?.content || '')
}
controller.close()
},
})
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
},
})
}
Edge Runtime es ligero y optimizado para streaming; en Docker suele ir más estable.
Verificar que funciona
Prueba con curl; si ves salida línea a línea, está bien:
curl -N http://yourdomain.com/api/chat \
-X POST \
-H "Content-Type: application/json" \
-d '{"message": "Hello"}'
-N desactiva el buffering del cliente; deberías ver contenido poco a poco, no de golpe tras una espera larga.
¿Sigue fallando? Revisa esto
- Proxy Cloudflare: la nube naranja también bufferiza. Desactívala (gris) o sube a Pro (streaming)
- Health check Docker: algunos health checks interfieren con conexiones en streaming; revisa
healthchecken docker-compose.yml - Balanceador de carga: un LB delante también puede bufferizar; configúralo aparte
Diagnóstico y corrección de problemas frecuentes
Varias trampas mías y preguntas habituales en la comunidad; cubren unos 80% de fallos de despliegue.
Problema 1: recursos estáticos 404
Síntoma: la página carga pero sin estilos; consola llena de 404 en /_next/static/...
Causa: el Dockerfile no copió bien .next/static.
Solución: comprueba estas dos líneas:
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
Si ya están y sigue el 404, revisa permisos:
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
Problema 2: fallo en docker build
Síntoma: docker build falla con “Could not find a production build in the ‘.next’ directory”
Causa: .dockerignore mal configurado o orden de build incorrecto.
Solución: crea .dockerignore excluyendo lo innecesario:
.next
node_modules
.git
.env*.local
out
.DS_Store
*.log
Nota: excluye .next porque el build se hace dentro del contenedor.
Problema 3: variables de entorno sin efecto
Síntoma: process.env.DATABASE_URL devuelve undefined
Causa: forma incorrecta de pasar variables o mezcla build vs runtime.
Solución:
- Runtime (BD, API keys):
docker run -eo docker-compose.yml:
docker run -e DATABASE_URL="..." your-image
- Build (
NEXT_PUBLIC_): endocker build:
docker build --build-arg NEXT_PUBLIC_API_URL="https://api.example.com" .
En el Dockerfile:
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
Problema 4: build falla por falta de memoria
Síntoma: el build se cuelga o “JavaScript heap out of memory”
Causa: límite de memoria por defecto de Node insuficiente para proyectos Next.js grandes.
Solución: más memoria en la fase builder del Dockerfile:
# En la fase builder
ENV NODE_OPTIONS="--max-old-space-size=4096"
RUN npm run build
O limita recursos con BuildKit:
docker build --memory=8g --memory-swap=8g -t your-image .
Problema 5: contenedor arranca pero no responde
Síntoma: contenedor en ejecución pero http://localhost:3000 rechaza la conexión.
Causa: Next.js escucha por defecto en 127.0.0.1; dentro del contenedor no es accesible desde fuera.
Solución: en el Dockerfile:
ENV HOSTNAME="0.0.0.0"
ENV PORT=3000
O al arrancar:
docker run -p 3000:3000 -e HOSTNAME="0.0.0.0" your-image
Comandos rápidos de diagnóstico
Ante dudas, ejecuta esto:
# 1. ¿Contenedor en ejecución?
docker ps
# 2. Logs del contenedor
docker logs <container-id>
# 3. Entrar y ver estructura de archivos
docker exec -it <container-id> sh
ls -la .next/
ls -la public/
# 4. Probar servicio dentro del contenedor
docker exec -it <container-id> wget -O- http://localhost:3000
# 5. Mapeo de puertos
docker port <container-id>
Conclusión
Migrar de Vercel a Docker autoalojado no fue tan terrible como imaginaba. La configuración inicial lleva tiempo, pero una vez estable, el mantenimiento es bajo. Ahora pago $12/mes fijos por el servidor, con tres proyectos Next.js, sin miedo a facturas disparadas.
Resumen de las tres configuraciones clave:
- Modo Standalone — una línea en
next.config.js; copia manual depublicy.next/static - Dockerfile multietapa — tres fases, imagen final ~200 MB, arranque rápido
- Proxy inverso — Nginx con buffering off (
proxy_buffering off); si no, el streaming muere
Si te atasacas con streaming, en el 99% es buffer del proxy; añade export const runtime = 'edge' y suele resolverse.
Vercel vs autoalojamiento
| Dimensión | Vercel | Docker autoalojado |
|---|---|---|
| Velocidad de despliegue | ⚡️ git push y listo | 🐢 5-10 min manual |
| Experiencia de desarrollo | 🌟 previews, logs, Analytics | 🔧 monitorización propia |
| Coste | 💸 $20+/mes; más caro con tráfico | 💰 $12/mes fijos (varios proyectos) |
| Escalabilidad | 📈 autoescalado | 📊 ajuste manual de recursos |
| Control | ⚠️ reglas de la plataforma | ✅ control total |
| Escenario ideal | Enterprise, servicio global | Personal, equipos pequeños, presupuesto limitado |
Recomendación final:
- Si eres desarrollador con varios side projects, autoalojar ahorra bastante
- Si el equipo no tiene ops o el tráfico es muy variable, quédate en Vercel
- No hay elección correcta o incorrecta; solo la que encaje contigo
Configuraciones completas y más detalle en repositorio GitHub (placeholder; sustituye al usarlo). Si tienes dudas, comenta; que otros no repitan las mismas trampas.
Flujo completo de despliegue autoalojado de Next.js con Docker
Pasos completos desde la configuración standalone hasta el despliegue en producción, con proxy inverso y corrección del streaming
⏱️ Estimated time: 2 hr
- 1
Step 1: Configurar el modo de salida Standalone
Activa el modo standalone en next.config.js:
1. Abre next.config.js
2. Añade la configuración: output: 'standalone'
3. Ejecuta el build: npm run build
4. Comprueba la salida: verifica que se haya generado .next/standalone
Puntos clave:
• El modo standalone no copia automáticamente public ni .next/static
• Esos dos directorios deben copiarse manualmente en el Dockerfile
• Si no, todos los recursos estáticos devolverán 404
Ejemplo de configuración:
```javascript
const nextConfig = {
output: 'standalone',
}
module.exports = nextConfig
``` - 2
Step 2: Crear un Dockerfile multietapa
Escribe un Dockerfile con build en tres fases:
Fase 1 — Instalación de dependencias:
• Usa node:20-alpine como imagen base
• Copia solo package.json y package-lock.json
• Ejecuta npm ci (aprovecha la caché de Docker)
Fase 2 — Build de la aplicación:
• Copia node_modules desde la fase 1
• Copia todo el código fuente
• Ejecuta npm run build
Fase 3 — Ejecución en producción:
• Crea un usuario no root (buena práctica de seguridad)
• Copia la carpeta public (recursos estáticos)
• Copia la salida .next/standalone
• Copia los archivos .next/static (CSS/JS compilados)
• Configura HOSTNAME="0.0.0.0" y PORT=3000
• Comando de arranque: node server.js
Puntos clave:
• El build en tres fases reduce la imagen de 1,5 GB a 200 MB
• Hay que copiar public y .next/static; si no, 404 en recursos estáticos
• Ejecuta con usuario no root para mayor seguridad - 3
Step 3: Configurar el proxy inverso Nginx
Configura Nginx como proxy inverso y desactiva el buffering:
1. Instala Nginx (o usa Caddy)
2. Configura el certificado SSL (Let's Encrypt)
3. Crea el archivo de configuración de Nginx
Configuración clave (obligatoria):
• proxy_buffering off; (desactivar buffering)
• proxy_cache off; (desactivar caché)
• proxy_set_header X-Accel-Buffering no; (indicar explícitamente a Nginx que no bufferice)
Cabeceras de petición necesarias:
• proxy_set_header Host $host;
• proxy_set_header X-Real-IP $remote_addr;
• proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
• proxy_set_header X-Forwarded-Proto $scheme;
Soporte WebSocket (si hace falta):
• proxy_set_header Upgrade $http_upgrade;
• proxy_set_header Connection "upgrade";
Probar la configuración:
```bash
nginx -t # probar sintaxis
nginx -s reload # recargar configuración
```
Nota: si no desactivas el buffering, el streaming dejará de funcionar - 4
Step 4: Gestionar variables de entorno
Distingue variables de build y de runtime:
Variables de build (prefijo NEXT_PUBLIC_):
• Deben pasarse en docker build
• Usa el parámetro --build-arg
• Declara en el Dockerfile: ARG NEXT_PUBLIC_API_URL
• Define la variable: ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
Variables de runtime (uso en servidor):
• Pásalas con docker run -e
• O configúralas en docker-compose.yml
• Léelas en el código desde process.env
En modo Standalone:
• runtimeConfig no funciona
• Debes usar el enfoque de App Router para leer variables
• Código de servidor: const dbUrl = process.env.DATABASE_URL
Ejemplo:
```bash
# Build
docker build --build-arg NEXT_PUBLIC_API_URL="https://api.example.com" .
# Runtime
docker run -e DATABASE_URL="postgres://..." your-image
``` - 5
Step 5: Corregir problemas de streaming
Soluciona el streaming que deja de funcionar (chat IA, SSE, etc.):
Síntomas:
• La salida en streaming no funciona; tarda mucho y aparece de golpe
• Server-Sent Events no se envían en tiempo real
Solución 1 — Configuración Nginx (obligatoria):
• Asegúrate de tener proxy_buffering off
• Asegúrate de tener X-Accel-Buffering: no
• Reinicia el servicio Nginx
Solución 2 — Usar Edge Runtime:
• Añade al inicio del archivo de ruta API: export const runtime = 'edge'
• Edge Runtime está optimizado para respuestas en streaming
• En entorno Docker se comporta de forma más estable
Verificar la corrección:
```bash
curl -N http://yourdomain.com/api/chat \
-X POST \
-H "Content-Type: application/json" \
-d '{"message": "Hello"}'
```
Con -N se desactiva el buffering; deberías ver la salida línea a línea
Otras comprobaciones:
• Proxy Cloudflare: si usas la nube naranja, también bufferiza (desactívala o sube a Pro)
• Health check de Docker: puede interferir con conexiones en streaming
• Balanceador de carga: si hay un LB delante, hay que configurarlo aparte - 6
Step 6: Desplegar y verificar
Construye la imagen y despliega:
1. Construir la imagen Docker:
```bash
docker build -t nextjs-app .
```
2. Ejecutar el contenedor:
```bash
docker run -d \
-p 3000:3000 \
-e DATABASE_URL="postgres://..." \
-e API_KEY="xxx" \
--name nextjs-app \
nextjs-app
```
3. Verificar el despliegue:
• Estado del contenedor: docker ps
• Ver logs: docker logs nextjs-app
• Probar acceso: curl http://localhost:3000
• Comprobar recursos estáticos: visita la ruta /_next/static/
4. Configurar Nginx y reiniciar:
• Asegúrate de que el proxy inverso esté bien configurado
• Prueba acceso HTTPS
• Verifica el streaming
5. Monitorización y mantenimiento:
• Reinicio automático del contenedor: --restart unless-stopped
• Revisa logs periódicamente
• Monitoriza recursos del servidor
Problemas frecuentes:
• 404 en recursos estáticos: comprueba que el Dockerfile copie public y .next/static
• Variables de entorno sin efecto: distingue build vs runtime
• Contenedor inaccesible: comprueba que HOSTNAME sea 0.0.0.0
FAQ
¿Cuánto se ahorra con autoalojamiento? ¿Cómo es la comparativa de costes?
¿Por qué los recursos estáticos devuelven 404? ¿Cómo arreglarlo?
• COPY --from=builder /app/public ./public
• COPY --from=builder /app/.next/static ./.next/static
Si sigue en 404, revisa permisos; usa --chown=nextjs:nodejs para el propietario correcto.
¿Qué hacer si el streaming no funciona?
1) En Nginx: proxy_buffering off; proxy_set_header X-Accel-Buffering no;
2) En rutas API usa Edge Runtime: export const runtime = 'edge'
3) Revisa el proxy Cloudflare (si lo usas): desactiva la nube naranja o sube a Pro
4) Verifica con curl -N; deberías ver salida línea a línea
¿Qué hacer si las variables de entorno no surten efecto?
• Prefijo NEXT_PUBLIC_: pásalas en docker build con --build-arg; declara ARG y ENV en el Dockerfile
• Variables de runtime: docker run -e o docker-compose.yml; léelas desde process.env
• En modo Standalone runtimeConfig no funciona; usa el enfoque App Router
¿Qué hacer si la imagen Docker es demasiado grande?
• Fase 1: solo instalar dependencias (caché Docker)
• Fase 2: construir la aplicación
• Fase 3: copiar solo lo necesario en runtime (standalone, public, static)
• La imagen final puede bajar de 1,5 GB a 200 MB
• Usa node:20-alpine como base para reducir más el tamaño
¿Cuándo conviene autoalojar y cuándo Vercel?
Vercel: sin capacidad de ops, tráfico muy variable con autoescalado, red edge global, funciones propias (Analytics, Edge Config), presupuesto holgado y prioridad en velocidad de desarrollo.
¿Qué hacer si el contenedor arranca pero no es accesible?
1) HOSTNAME debe ser 0.0.0.0 (no 127.0.0.1); en Dockerfile: ENV HOSTNAME="0.0.0.0"
2) Mapeo de puertos: docker run -p 3000:3000
3) Contenedor en ejecución: docker ps
4) Logs: docker logs <container-id>
5) Prueba interna: docker exec -it <container-id> wget -O- http://localhost:3000
11 min de lectura · Publicado el: 20 dic 2025 · Actualizado el: 21 ago 2026
Guía completa de Next.js
Si llegaste desde búsqueda, lo más rápido es ir al artículo anterior o siguiente de esta misma serie.
Anterior
Guía práctica de CI/CD para Next.js: pruebas y despliegue automáticos con GitHub Actions
Automatiza pruebas y despliegue de proyectos Next.js con GitHub Actions: configuración completa, lecciones aprendidas y buenas prácticas. Olvídate del despliegue manual: haz push y tu código sale en producción.
Parte 41 de 51
Siguiente
Guía completa de monitorización en producción con Next.js: integración de Sentry, gestión de logs y alertas
Te guiamos paso a paso para montar un sistema de monitorización en producción con Next.js: integración de Sentry, gestión de logs, monitorización de rendimiento y alertas. Configuración práctica con App Router y plantillas de código listas para usar
Parte 43 de 51



Comentarios
Inicia sesión con GitHub para dejar un comentario