// DevOps

Despliegue moderno de Next.js: GitHub Actions, Docker y cero tiempo de inactividad

Publicado el 22.09.2026

Si todavía haces next build directamente en el servidor de producción — tu servidor realmente sufre. CPU al máximo, OOM-kill, errores 502 y largos tiempos de inactividad — son clásicos a los que ya es hora de poner fin.

En 2026 el estándar de la industria — es la compilación separada:

  1. Construimos una imagen standalone mínima en la nube con GitHub Actions.
  2. La empujamos a GHCR (GitHub Container Registry).
  3. En el servidor solo hacemos pull + reinicio atómico.

Capítulo 1. Dockerfile: multi-stage y standalone

Se obtiene una imagen pequeña y rápida — en modo standalone. Next.js calcula por sí mismo qué archivos y partes de node_modules son realmente necesarios para que funcione el servidor, y copia solo esos.

dockerfile
# syntax=docker/dockerfile:1
# ЭТАП 1 — Dependencias
FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json yarn.lock* pnpm-lock.yaml* ./
RUN \
  if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
  elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
  else npm ci; \
  fi

# ЭТАП 2 — Compilación
FROM node:24-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

# ЭТАП 3 — Imagen de producción (Runner)
FROM node:24-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

# Seguridad ante todo: no trabajamos como root
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs

# Copiamos solo los artefactos de la compilación standalone
COPY --from=builder --chown=nextjs:nodejs /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"

# Comprobación de salud del contenedor
HEALTHCHECK --interval=15s --timeout=3s --start-period=10s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1

CMD ["node", "server.js"]

La imagen node:24-alpine — está en la rama LTS actual de Node.js. Verifica el soporte de la versión elegida en endoflife.date/nodejs: en septiembre de 2026 están soportadas las ramas 24, 22 y 20.

Qué aporta

  • Peso: La imagen pesa ~200 MB frente a 1.5 GB de la habitual.
  • Seguridad: Usar el usuario non-root (nextjs) protege el sistema host en caso de compromiso del contenedor.
  • Healthcheck: Docker detectará si la aplicación “se quedó colgada” al arrancar y no enviará tráfico hacia ella.

Capítulo 2. Análisis del workflow de GitHub Actions

Tu pipeline está dividido en dos etapas (jobs): construcción en la nube y despliegue en tu “hardware”.

1. Preparación y build

yaml
- name: Docker meta & tags
  id: prepare
  run: |
    IMAGE_REPO="ghcr.io/${GITHUB_REPOSITORY,,}"
    SHORT_SHA="${GITHUB_SHA::8}"
    echo "image_repo=$IMAGE_REPO" >> $GITHUB_OUTPUT
    echo "image_tag=sha-$SHORT_SHA" >> $GITHUB_OUTPUT

Importante: La sintaxis ${GITHUB_REPOSITORY,,} convierte el nombre del repositorio a minúsculas. Los registros de Docker no aceptan letras mayúsculas, y en GitHub las mayúsculas son frecuentes.


2. Caché de la compilación

yaml
cache-from: type=gha
cache-to: type=gha,mode=max

Usamos el caché nativo de GitHub Actions. Si no cambiaste package.json, la etapa de instalación de dependencias se omitirá y el build tardará 1–2 minutos en lugar de 10.


3. Despliegue con comprobación de salud (bucle de healthcheck)

La parte más importante — no solo le decimos al servidor “actualízate”, comprobamos si la aplicación sobrevivió.

bash
for i in {1..60}; do
  STATUS=$(docker inspect --format='{{json .State.Health.Status}}' nextjs 2>/dev/null || echo '"not-found"')
  if [[ $STATUS == '"healthy"' || $STATUS == '"no-healthcheck"' ]]; then
    HEALTHY=1
    break
  fi
  sleep 2
done

Si Next.js falla por un error en las variables de entorno, el script lo detectará, no actualizará el proxy (Caddy/Nginx) y finalizará la action con error. Tu sitio antiguo seguirá funcionando y recibirás una notificación del problema.


Capítulo 3. Variables en tiempo de ejecución vs variables en tiempo de compilación

Esto es una trampa en la que casi todos caen.

  1. NEXT_PUBLIC_ (Build-time): Estas variables se incorporan en el bundle JS durante el comando next build. Si las cambias en el servidor en .env, no pasará nada. Deben pasarse en GitHub Actions como build-args.

  2. Secretos (Runtime): DATABASE_URL, JWT_SECRET. No se deben incluir en la imagen Docker. Se inyectan en el momento de ejecutar el contenedor mediante docker-compose.

Consejo: Cuando sea posible, haz que la URL del API también sea una variable en tiempo de ejecución mediante proxy o scripts de configuración, para que la misma imagen pueda desplegarse tanto en staging como en producción sin recompilarse.

Temas relacionados se tratan en el blog: Dockerfile correcto, CI/CD de despliegue manual a automático y despliegue de contenedores con Kamal.

// Reviews

Reseñas relacionadas

Llegué con una solicitud costosa para la configuración de un servidor VPS, pero durante la consulta Mikhail propuso una solución mucho más sencilla y económica. Al final ahorré dinero y tiempo. Mikhail — un verdadero experto que trabaja por el resultado del cliente, no por la factura. ¡Lo recomiendo!

Llegué con una solicitud costosa para la configuración de un servidor VPS, pero durante la consulta Mikhail propuso una solución mucho más simple y económica. Al final ahorré presupuesto y tiempo. Mikhail es un …

kfhzasorin

Configuración de VPS, configuración del servidor

12.05.2026 · ★ 5/5

Hubo varios problemas, tanto en la parte técnica como en la comprensión general. Mijaíl respondió rápido a la solicitud, ayudó a aclarar las cosas y resolvió los problemas técnicos; por ello, muchas gracias. Estoy satisfecho con el resultado.

Hubo varios problemas relacionados tanto con la parte técnica como con la comprensión en general. Mijaíl respondió rápidamente a la solicitud, ayudó a aclarar las cosas y resolvió los problemas técnicos, por lo que le …

abazawolf

Configuración de VPS, configuración del servidor

18.02.2026 · ★ 5/5

// Contact

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema

Respondo en un día laborable (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirme que no es un bot.

Escribir y recibir una respuesta rápida