Saltar al contenido

Deploy SSR y edge: Workers, Vercel, Node y Docker

Publicar un sitio con renderizado bajo demanda en dos familias de runtime: el edge de Cloudflare Workers, Vercel o Netlify Functions, y un servidor Node de larga vida en un VPS o contenedor Docker. Qué adapter elige cada destino, en qué se diferencian el edge y el servidor persistente, y cómo empaquetar el handler con Docker.

Dos familias de runtime para el mismo handler

Tu código de servidor no cambia según dónde lo despliegues —sigue leyendo Astro.request y devolviendo un Response—, pero el entorno que lo ejecuta sí, y se divide en dos familias con caracteres opuestos. El adapter es lo que traduce entre tu handler universal y la familia concreta que elijas.

La primera familia es efímera y distribuida: la función no existe hasta que llega una petición, se ejecuta cerca del usuario y muere enseguida. La segunda es persistente y centralizada: un proceso Node que arrancas una vez y que atiende peticiones hasta que lo pares. Esa diferencia de vida es la raíz de todo lo demás.

De esa raíz brotan cuatro consecuencias que conviene tener presentes al elegir:

  • Arranque: casi instantáneo en el edge; el servidor Node ya está caliente, pero hay que mantenerlo vivo.
  • Estado: el edge no recuerda nada entre peticiones; Node puede guardar caché o conexiones en memoria.
  • Escalado: el edge se multiplica solo por petición; el servidor lo escalas tú, con más instancias o réplicas.
  • APIs: el edge ofrece un subconjunto web; Node te da la plataforma completa y cualquier módulo nativo.

Edge: efímero, cercano y con límites

Instalar un adapter de edge es una línea, y el build cambia su salida para producir funciones en lugar de un servidor completo.

Shell
npx astro add cloudflare# o vercel, o netlify: cada uno produce el formato de su plataforma

El edge compra dos ventajas enormes: latencia —tu código corre en el punto de presencia más cercano al visitante, no en un centro de datos lejano— y escalado sin esfuerzo —cada petición arranca su propia instancia, así que mil peticiones simultáneas son mil funciones, sin que tú provisiones nada—. A cambio, impone una física estricta que se resume en tres límites:

  • Sin Node completo: el runtime son APIs web, así que módulos nativos y librerías que dependan de binarios de Node no cargan.
  • Techo de CPU: cada invocación tiene un tiempo máximo, de modo que un cómputo largo o un procesado pesado no encaja.
  • Sin memoria persistente: la función no recuerda nada entre peticiones; el estado vive fuera, en KV, en una base de datos o en un almacén externo.

Ninguno de esos límites es un defecto: son el precio exacto de la ubicuidad. Una función que no guarda estado y termina rápido es justo la que puede replicarse en cientos de sitios sin coordinación.

Node en un VPS o contenedor

La otra familia es el servidor Node de siempre: un proceso de larga vida que arranca, escucha en un puerto y atiende peticiones hasta que lo detienes. Es el destino que da control total —Node completo, disco, estado en memoria, cualquier librería— a cambio de que tú te hagas cargo de mantenerlo vivo.

JS
// astro.config.mjsimport { defineConfig } from 'astro/config';import node from '@astrojs/node';export default defineConfig({  adapter: node({ mode: 'standalone' }),});

En modo standalone, el build produce un servidor autónomo que arrancas con un solo comando. Eso lo hace ideal para meterlo en un contenedor: una imagen Docker que empaqueta Node, tus node_modules y tu dist/, y que cualquier orquestador puede correr igual en tu portátil que en producción.

DOCKERFILE
# Etapa de build: instala todo y construyeFROM node:22-alpine AS buildWORKDIR /appCOPY package*.json ./RUN npm ciCOPY . .RUN npm run build# Etapa final: solo lo necesario para ejecutarFROM node:22-alpineWORKDIR /appCOPY --from=build /app/dist ./distCOPY --from=build /app/node_modules ./node_modulesENV HOST=0.0.0.0ENV PORT=4321EXPOSE 4321CMD ["node", "./dist/server/entry.mjs"]

El patrón de dos etapas —multi-stage— es lo que separa una imagen profesional de una pesada: la primera etapa tiene todo lo necesario para construir, pero la imagen final solo copia el dist/ y los node_modules, dejando fuera el código fuente y las herramientas de build. El resultado es una imagen pequeña, reproducible y arrancable en cualquier sitio que hable Docker: un VPS con un docker run, un Kubernetes, un servicio de contenedores gestionado.

El precio de todo ese control es la responsabilidad. Un servidor persistente que tú posees también eres tú quien lo mantiene arriba: vigilar que el proceso no muera, reiniciarlo si cae, parchear el sistema, escalar cuando el tráfico crece. El edge te quita ese trabajo a cambio de sus límites; el servidor te da libertad a cambio de operarlo. Elegir entre ambos no es elegir el mejor, sino decidir qué prefieres tener en tus manos: la comodidad de no mantener nada o el control de mantenerlo todo.