Despliegue
Receta
Implementa una aplicación Next.js 15+ App Router con modo de salida standalone, contenedores Docker, funciones de edge runtime y optimizaciones específicas de plataforma.
Ejemplo Funcional
Configuración de Compilación Standalone
// next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
};
export default nextConfig;# Compila la aplicación
npm run build
# La salida standalone está en .next/standalone
# Copia los assets estáticos y públicos
cp -r .next/static .next/standalone/.next/static
cp -r public .next/standalone/public
# Ejecuta el servidor standalone
node .next/standalone/server.jsDockerfile de Producción
# Dockerfile
FROM node:20-alpine AS base
# Instala las dependencias
FROM base AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
# Compila la aplicación
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# Imagen de producción
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]# docker-compose.yml
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/mydb
- AUTH_SECRET=your-secret-here
depends_on:
- db
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:Ruta de Edge Runtime
// app/api/geo/route.ts
import { NextRequest, NextResponse } from "next/server";
export const runtime = "edge";
export async function GET(request: NextRequest) {
return NextResponse.json({
city: request.geo?.city ?? "unknown",
country: request.geo?.country ?? "unknown",
region: request.geo?.region ?? "unknown",
});
}Middleware de Edge Runtime
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
// El middleware siempre se ejecuta en el edge
export function middleware(request: NextRequest) {
const country = request.geo?.country ?? "US";
const response = NextResponse.next();
response.headers.set("x-user-country", country);
return response;
}Script de Análisis de Compilación
// package.json
{
"scripts": {
"build": "next build",
"analyze": "ANALYZE=true next build",
"start": "next start",
"start:standalone": "node .next/standalone/server.js"
}
}// next.config.ts (con analizador de paquetes)
import type { NextConfig } from "next";
import withBundleAnalyzer from "@next/bundle-analyzer";
const nextConfig: NextConfig = {
output: "standalone",
};
export default withBundleAnalyzer({
enabled: process.env.ANALYZE === "true",
})(nextConfig);Explicación Profunda
Cómo Funciona
output: "standalone"produce una compilación autónoma que incluye solo los archivosnode_modulesnecesarios. La salida en.next/standalonepuede ejecutarse con solonode server.js, sin necesidad del directorionode_modulescompleto.- Los assets estáticos (
.next/static) ypublic/no se incluyen en la salida standalone. Deben copiarse por separado o servirse desde un CDN. - El Edge Runtime utiliza un aislado V8 simplificado (no Node.js). Comienza más rápido y tiene menos overhead de memoria, pero no puede usar APIs de Node.js como
fs,Buffero módulos nativos. - Las compilaciones Docker de múltiples etapas reducen el tamaño de la imagen final al separar la instalación de dependencias, la compilación y la imagen de runtime. Una imagen Docker Next.js standalone típica es de 100-200 MB.
HOSTNAME="0.0.0.0"es necesario en Docker para aceptar conexiones desde fuera del contenedor. Sin él, el servidor se vincula a127.0.0.1y es inaccesible.
Variaciones
Exportación Estática (Sin Servidor):
// next.config.ts
const nextConfig: NextConfig = {
output: "export",
};Esto genera un sitio completamente estático en el directorio out/. No se requiere servidor Node.js. Limitaciones: sin Server Components en el tiempo de solicitud, sin rutas API, sin middleware, sin ISR.
Despliegue en Vercel:
# Instala la CLI de Vercel
npm i -g vercel
# Despliega (detección automática de Next.js)
vercel
# Despliega en producción
vercel --prodVercel maneja automáticamente la salida standalone, las funciones edge y la distribución de CDN. No se necesita Dockerfile.
Punto Final de Verificación de Salud Personalizado:
// app/api/health/route.ts
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({
status: "ok",
timestamp: new Date().toISOString(),
version: process.env.APP_VERSION ?? "unknown",
});
}Notas de TypeScript
request.geoestá tipado como{ city?: string; country?: string; region?: string; latitude?: string; longitude?: string } | undefined. Solo se completa en despliegues edge (Vercel, Cloudflare).- La exportación
runtimedebe ser un literal de string ("edge"o"nodejs"). No puede ser una variable.
Problemas Comunes
- El modo standalone no incluye
public/o.next/static/. Debes copiar estos directorios en la salida standalone o servirlos desde un CDN. - Edge Runtime no admite todas las APIs de Node.js.
fs,path,child_process,crypto(usaglobalThis.crypto), y la mayoría de módulos nativos no están disponibles. Consulta la referencia de API de Edge Runtime de Next.js. - Las variables de entorno en Docker se incrustan en el tiempo de compilación. Para variables de entorno de runtime, establécelas en el contenedor (
docker run -e KEY=value) y accede a ellas a través deprocess.enven el código del servidor. Las variablesNEXT_PUBLIC_siempre son solo de tiempo de compilación. - El servidor standalone no sirve archivos estáticos. Configura un proxy inverso (nginx, Caddy) o CDN para servir las rutas
/_next/static/y/public/. output: "export"desactiva todas las características del servidor. Sin Server Components en el tiempo de solicitud, sin Route Handlers, sin middleware, sin ISR. Solo úsalo para sitios completamente estáticos.- La optimización de imágenes requiere Sharp en modo standalone. Añade
sharpa tus dependencias:npm install sharp.
Alternativas
| Enfoque | Ventajas | Desventajas |
|---|---|---|
| Vercel | Cero configuración, edge-nativo, CDN automático | Bloqueo de proveedor, costo en escala |
| Docker + standalone | Portátil, cualquier proveedor en la nube | Configuración de infraestructura manual, sin edge por defecto |
Exportación estática (output: "export") | Sin servidor, hospedaje barato (S3, Cloudflare) | Sin características del servidor |
| AWS Amplify | Administrado, admite SSR | Configuración de edge limitada, específico de AWS |
| Cloudflare Pages | Primero en edge, rápido, nivel gratuito generoso | Soporte limitado de API de Node.js |
| Railway o Fly.io | Basado en Docker, DX simple | Ecosistema más pequeño |
Preguntas Frecuentes
¿Qué hace output: "standalone" y por qué es necesario para Docker?
- Produce una compilación autónoma que incluye solo los archivos
node_modulesnecesarios. - La salida puede ejecutarse con solo
node server.js, sin el directorionode_modulescompleto. - Esto reduce dramáticamente el tamaño de la imagen Docker (típicamente 100-200 MB).
¿Por qué debes copiar .next/static y public/ por separado en modo standalone?
- La salida standalone incluye solo código del lado del servidor y dependencias mínimas.
- Los assets estáticos y archivos públicos se excluyen para permitir servirlos desde un CDN.
- Debes copiarlos al directorio standalone o configurar un proxy inverso para servirlos.
¿Qué es HOSTNAME="0.0.0.0" y por qué es necesario en Docker?
- Por defecto, el servidor Next.js se vincula a
127.0.0.1(solo localhost). - Dentro de un contenedor Docker,
127.0.0.1es inaccesible desde fuera del contenedor. - Establecer
HOSTNAME="0.0.0.0"hace que el servidor acepte conexiones desde cualquier interfaz de red.
¿Qué es el Edge Runtime y cuáles son sus limitaciones?
- El Edge Runtime utiliza aislados V8 (no Node.js) para cold starts más rápidos y menos memoria.
- No puede usar APIs de Node.js como
fs,path,child_processoBuffer. - Usa
globalThis.cryptoen lugar del módulocryptode Node.js.
Problema Común: ¿Por qué las variables NEXT_PUBLIC_ no funcionan como variables de entorno de runtime de Docker?
- Los valores
NEXT_PUBLIC_se reemplazan estáticamente en el tiemponext build. - Establecerlos como variables de entorno de runtime en el contenedor no tiene efecto.
- Debes reconstruir la imagen Docker cada vez que cambia un valor
NEXT_PUBLIC_.
¿Qué produce output: "export" y qué características desactiva?
- Genera un sitio completamente estático en el directorio
out/sin servidor necesario. - Desactiva Server Components en el tiempo de solicitud, Route Handlers, middleware e ISR.
- Solo úsalo para sitios completamente estáticos.
¿Por qué se necesita Sharp en modo standalone para la optimización de imágenes?
- Next.js usa Sharp para cambiar el tamaño de imágenes sobre la marcha y conversión de formato.
- El modo standalone no incluye Sharp automáticamente.
- Añádelo explícitamente con
npm install sharpo las imágenes no se optimizarán.
¿Cómo escribes el tipo de la exportación runtime para un Route Handler de Edge en TypeScript?
export const runtime = "edge";
// o
export const runtime = "nodejs";- El valor debe ser un literal de string, no una variable.
- TypeScript lo aplicará como
"edge" | "nodejs".
¿Cuál es el propósito del patrón de compilación Docker de múltiples etapas que se muestra en esta página?
- Etapa 1 (
deps) instala solo las dependencias de producción. - Etapa 2 (
builder) copia deps y compila la aplicación. - Etapa 3 (
runner) copia solo la salida standalone, reduciendo el tamaño de la imagen final. - Cada etapa descarta los artefactos de tiempo de compilación no necesarios en producción.
Problema Común: ¿Por qué el servidor standalone no sirve archivos estáticos por defecto?
- El
server.jsstandalone es un servidor Node.js mínimo enfocado en SSR. - El servicio de archivos estáticos se espera que sea manejado por un CDN o proxy inverso (nginx, Caddy).
- Servir archivos estáticos a través de Node.js es menos eficiente que un servidor estático dedicado.
¿Cómo funciona request.geo y cuándo se completa?
export async function GET(request: NextRequest) {
const city = request.geo?.city ?? "unknown";
}request.geosolo se completa en despliegues edge (Vercel, Cloudflare).- Es
undefineden desarrollo local y despliegues de Node.js no-edge. - Siempre usa encadenamiento opcional y proporciona valores predeterminados.
¿Cuál es el propósito de un punto final de verificación de salud en despliegues de producción?
- Los balanceadores de carga y orquestadores de contenedores (Kubernetes, ECS) lo usan para verificar que la aplicación se está ejecutando.
- Devuelve una simple respuesta
{ status: "ok" }con metadatos opcionales. - Una verificación de salud fallida activa reinicios automáticos de contenedores o redirección de tráfico.
Relacionado
- Variables de Entorno - configuración de variables de entorno para producción
- SEO - metadatos y sitemaps para sitios de producción
- Manejo de Errores - monitoreo de errores de producción
- Documentación de Despliegue de Next.js