Cambiar tema

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

Easton editorial illustration: performance inspection lens

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.

$35-50
Coste mensual Vercel
Según tráfico
$12
Coste mensual autoalojado
Coste fijo
$300-500
Ahorro anual
Varios proyectos en el mismo servidor
200MB
Tamaño imagen Docker
Tras build multietapa optimizado
Source: Datos de producción

¿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 arranque
  • node_modules reducido: 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:

  1. Build en tres fases: dependencias, build y runtime separados; la imagen final solo lleva lo necesario, de ~1,5 GB a ~200 MB
  2. COPY --from=builder /app/public: no lo olvides; sin esto favicon, robots.txt, etc. no cargan
  3. COPY ./.next/static: aún más crítico; sin esto todo JS/CSS da 404
  4. 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?

  1. Seguridad: filtra peticiones maliciosas, rate limiting, anti-DDoS
  2. HTTPS: gestión centralizada de certificados SSL
  3. Varias apps: un servidor, varios proyectos por dominio/ruta
  4. 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:

  1. proxy_buffering off: sin buffering, el streaming no se bloquea
  2. X-Accel-Buffering: no: indica a Nginx que no bufferice el cuerpo de la respuesta
  3. WebSocket: con Socket.io o tiempo real, la cabecera Upgrade es 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:

  1. Buffer del proxy inverso: Nginx bufferiza el cuerpo por defecto y envía al cliente cuando está completo
  2. 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

  1. Proxy Cloudflare: la nube naranja también bufferiza. Desactívala (gris) o sube a Pro (streaming)
  2. Health check Docker: algunos health checks interfieren con conexiones en streaming; revisa healthcheck en docker-compose.yml
  3. 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:

  1. Runtime (BD, API keys): docker run -e o docker-compose.yml:
   docker run -e DATABASE_URL="..." your-image
   
  1. Build (NEXT_PUBLIC_): en docker 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:

  1. Modo Standalone — una línea en next.config.js; copia manual de public y .next/static
  2. Dockerfile multietapa — tres fases, imagen final ~200 MB, arranque rápido
  3. 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ónVercelDocker 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 idealEnterprise, servicio globalPersonal, 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. 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. 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. 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. 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. 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. 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?
Vercel cuesta $35-50/mes (según tráfico); Docker autoalojado $12/mes fijos (varios proyectos en el mismo servidor). Ahorro mensual de $25-40, $300-500 al año. Lo importante: el coste es fijo y no se dispara con picos de tráfico. Ideal para proyectos personales, equipos pequeños y presupuestos ajustados.
¿Por qué los recursos estáticos devuelven 404? ¿Cómo arreglarlo?
El modo standalone no copia automáticamente public ni .next/static. Solución: en la fase runner del Dockerfile añade:
• 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?
En el 99% de los casos es el buffer del proxy inverso. Solución:
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?
Distingue build y runtime:
• 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?
Optimiza con build multietapa:
• 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?
Autoalojar: base Linux/Docker, tráfico estable, presupuesto ajustado, despliegue manual aceptable, proyectos personales/equipos pequeños.
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?
Comprueba:
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

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog