// DevOps
Despliegue moderno de Next.js: GitHub Actions, Docker y cero tiempo de inactividad
Publicado el 02.03.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 — es un clásico que ya es hora de terminar.
En 2026, el estándar de la industria es la construcción separada:
- Construimos una imagen standalone mínima en la nube con GitHub Actions.
- La empujamos a GHCR (GitHub Container Registry).
- En el servidor solo hacemos pull + reinicio atómico.
Capítulo 1. Dockerfile ideal (multietapa + standalone)
Todo el secreto de una imagen pequeña y rápida está en el 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.
# syntax=docker/dockerfile:1
# ETAPA 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
# ETAPA 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
# ETAPA 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 ejecutar 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"]
¿Por qué es genial?
- Tamaño: La imagen pesa ~200 MB frente a 1.5 GB de la habitual.
- Seguridad: Usar un usuario
non-root(nextjs) protege el sistema anfitrión en caso de compromiso del contenedor. - Healthcheck: Docker detectará si la aplicación se ‘colgó’ al iniciar y no enviará tráfico a ella.
Capítulo 2. Análisis del workflow de GitHub Actions
Tu pipeline está dividido en dos etapas (jobs): la construcción en la nube y el despliegue en tu “hardware”.
1. Preparación y build
- 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 mayúsculas, y en GitHub a menudo las hay.
2. Caché de la compilación
cache-from: type=gha
cache-to: type=gha,mode=max
Usamos el caché nativo de GitHub Actions. Si no has cambiado package.json, la etapa de instalación de dependencias se omitirá y el build durará 1–2 minutos en lugar de 10.
3. Despliegue inteligente (bucle de healthcheck)
El punto más importante — no solo decimos al servidor “actualízate”, comprobamos si la aplicación sobrevivió.
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 se cae por un error en las variables de entorno, el script lo detectará, no actualizará el proxy (Caddy/Nginx) y terminará la acción 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 en tiempo de compilación
Estas son las trampas en las que casi todos caen.
NEXT_PUBLIC_(Build-time): Estas variables se “incrustan” en el bundle JS durante el comandonext build. Si las cambias en el servidor en.env, no cambiará nada. Debes pasarlas en GitHub Actions comobuild-args.Secretos (Runtime):
DATABASE_URL,JWT_SECRET. No se deben meter en la imagen Docker. Se inyectan en el momento de arrancar el contenedor mediantedocker-compose.
Consejo: Si es posible, haz que la URL de la API también sea una variable en tiempo de ejecución mediante proxy o scripts de configuración especiales, para que la misma imagen pueda desplegarse tanto en staging como en producción sin reconstrucción.
// Reviews
Reseñas relacionadas
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 …
Configuración de VPS, configuración del servidor
18.02.2026 · ★ 5/5
Todo se hizo de manera rápida y precisa. Lo recomiendo.
Todo se hizo rápido y con precisión. Lo recomiendo.
Configuración de VPS, configuración del servidor
17.01.2026 · ★ 5/5
Todo salió bien, el profesional respondió rápidamente a las preguntas y ayudó a resolver el problema. ¡Gracias!
Todo fue bien, el profesional respondió rápidamente a las preguntas y ayudó a resolver el problema. ¡Gracias!
Configuración de VPS, configuración del servidor
16.12.2025 · ★ 5/5
Lo hicieron todo con rapidez. Seguiremos acudiendo. ¡Lo recomiendo!
Todo lo hicieron con rapidez. Seguiremos acudiendo. ¡Lo recomiendo!
Configuración de VPS, configuración del servidor
10.12.2025 · ★ 5/5
Hicieron todo rápidamente. Mijaíl siempre está disponible. Seguiremos recurriendo a él.
Todo se hizo con rapidez. Михаил siempre está en contacto. Seguiremos recurriendo a él
Configuración de VPS, configuración del servidor
10.12.2025 · ★ 5/5
¡Mijaíl es un profesional! Ya no es la primera vez que lo demuestra en la práctica.
Михаил, ¡un profesional! Ya lo ha demostrado en la práctica más de una vez.
// Contact
¿Necesitas ayuda?
Escríbeme y te ayudaré a resolver el problema
// Related