¿Por qué autoalojar Nuxt en un VPS?
Con Nitro, Nuxt genera un build portable (preset: node-server) que arranca como un simple servidor Node a la escucha en un puerto. Esta portabilidad es ideal para un VPS: dispone del renderizado del lado del servidor, de las rutas server/api y de la caché de rutas sin depender de un runtime edge propietario.
El autoalojamiento elimina las cuotas de funciones y le permite ajustar el almacenamiento en caché, la compresión y el número de instancias Node. Para una aplicación Vue 3 en producción con autenticación, llamadas a API de servidor y un SEO cuidado, el VPS ofrece la estabilidad de un servidor persistente y un costo fijo, sin sorpresas de facturación por invocación.
Aun así, hace falta un servidor que acepte un proceso Node.js permanente: la mayoría de los alojamientos compartidos no lo permiten, lo que descarta desde el principio el SSR y las rutas de servidor. En un VPS también elige la versión de Node.js, el proxy inverso (Nginx o Caddy) y la gestión de los certificados TLS, sin un tope artificial de CPU o memoria.
Beneficios concretos de un Nuxt autoalojado
- Rutas de servidor
server/apiilimitadas, sin facturación por llamada. - SSR e hidratación Vue 3 servidos desde un servidor Node persistente y estable.
- Build Nitro portable (
node-server), fácil de contenerizar y de reproducir. - Caché de rutas y reglas
routeRules(SSR, SSG, ISR, SWR) controladas desde el servidor. - Varias apps Nuxt en un mismo VPS detrás de Nginx, cada una en su puerto interno (3000, 3001…).
- Variables de entorno en runtime (
runtimeConfig) gestionadas directamente en su servidor. - Control total sobre las cabeceras HTTP (CSP, HSTS, cache-control) vía middleware Nitro.
- Un sitio pregenerado con
nuxt generate, servido solo por Nginx, sin proceso Node.js permanente.
SSR, SSG, SPA o híbrido: ¿qué modo de renderizado elegir?
Desplace la tabla
| Modo | Renderizado | Proceso Node.js | Ideal para |
|---|---|---|---|
| SSR (por defecto) | En cada petición, en el servidor | Sí, supervisado por PM2 | Aplicación dinámica, SEO, contenido personalizado |
| SSG (`nuxt generate`) | En el build, como HTML estático | No, Nginx sirve `.output/public` | Blog, web corporativa, contenido que cambia poco |
| SPA (`ssr: false` + `nuxt generate`) | En el navegador | No, basta con Nginx | Panel interno, aplicación tras autenticación |
| Híbrido (`routeRules`) | Por ruta: SSR, prerenderizado, SWR, ISR | Sí, servidor Nitro | Aplicación que mezcla páginas estáticas y rutas API |
Requisitos de hardware y software
Como con cualquier framework moderno, el build de Nuxt consume memoria: apunte a 2 GB de RAM como mínimo (el build puede hacerse en CI si su VPS es más modesto). En runtime, 1 vCPU y 1 GB bastan para el servidor Nitro con carga ligera; suba a 2 vCPU / 2 GB para aplicaciones con muchas rutas server/api simultáneas, y a 2 vCPU / 4 GB si varias aplicaciones Nuxt comparten el VPS.
En cuanto a versiones, parta de Nuxt 4: Nuxt 3 llegó al final de su vida útil el 31 de julio de 2026 y ya no recibe correcciones ni parches de seguridad. El despliegue descrito aquí es el mismo para ambas versiones, ya que es Nitro quien produce el servidor. Instale una línea LTS de Node.js que siga mantenida, Node.js 24 o 26 (Node.js 20 está fuera de soporte desde el 30 de abril de 2026), mediante nvm o una imagen node:24-alpine, junto con PM2 para la supervisión de procesos. node-server es el preset que Nitro aplica por defecto a un build de producción: declararlo en nuxt.config.ts solo lo hace explícito. Se requiere un dominio que apunte a su VPS para el certificado TLS. Ubuntu 22.04 o 24.04 LTS sigue siendo la base recomendada.
Partir de la plantilla Nuxt Stack
La plantilla Nuxt Stack del Marketplace de ServOrbit prepara un VPS Ubuntu 24.04 con Node.js en la línea LTS actual del repositorio de NodeSource, PM2 instalado de forma global, Nginx y PostgreSQL. Le ahorra la instalación del runtime: los pasos siguientes empiezan entonces en la configuración del proyecto.
Es un entorno de ejecución, no una aplicación con pantalla de inicio de sesión: se accede por SSH (ssh root@<ip-del-vps>) para subir el código. En el primer acceso, Nginx sirve su página por defecto en el puerto 80; quedan por preparar el virtual host de su aplicación, el certificado de Let's Encrypt (Certbot no viene preinstalado) y su código. PostgreSQL solo escucha en local, con autenticación peer para el usuario del sistema postgres (sudo -u postgres psql): suficiente para conectar una DATABASE_URL en 127.0.0.1 sin exponer la base de datos.
Desplegar Nuxt paso a paso
Configurar el proyecto para Node
Defina el preset Nitro en
nuxt.config.ts:export default defineNuxtConfig({ nitro: { preset: 'node-server' } })Declare sus secretos en
runtimeConfigúnicamente en el lado del servidor — nunca enruntimeConfig.public. En el VPS, instale Node.js 24 LTS connvm install 24 && nvm use 24 && nvm alias default 24, luego PM2 de forma global:npm install -g pm2. En la plantilla Nuxt Stack, Node.js y PM2 ya están instalados.Compilar la aplicación
Clone el repositorio e instale las dependencias:
git clone https://git.sudominio.com/su-org/su-app.git /srv/nuxt-app cd /srv/nuxt-app npm ci npm run buildNitro produce una carpeta
.outputautónoma que contieneserver/index.mjsy los assets estáticos en.output/public. Esa carpeta es todo lo que el VPS necesita para servir la aplicación en producción — también puede ensamblarla en CI y transferirla por rsync para evitar el build en el servidor.Crear el archivo de entorno
Cree
/srv/nuxt-app/.envcon sus variables de runtime. Todas las variables con prefijoNUXT_sobreescriben las claves correspondientes deruntimeConfigal arrancar:NUXT_MY_SECRET_KEY=valor-produccion NITRO_HOST=127.0.0.1 NITRO_PORT=3000Nunca incluya este archivo en el repositorio. Asegúrese de que pertenece al usuario que ejecuta PM2 y solo es legible por ese usuario (
chmod 600).Con
NITRO_HOST=127.0.0.1, Nitro solo escucha en la interfaz local: únicamente Nginx puede alcanzarlo y el puerto 3000 no queda expuesto a Internet.Arrancar el servidor Nitro con PM2
Inicie la aplicación en modo cluster pidiendo a Node que cargue el archivo de entorno: Nuxt no lee
.enven producción y la línea de comandos de PM2 no tiene una opción dedicada.pm2 start /srv/nuxt-app/.output/server/index.mjs \ --name nuxt-app -i max \ --node-args="--env-file=/srv/nuxt-app/.env" pm2 save pm2 startup-i maxlanza una instancia por vCPU; este modo cluster es lo que permite las recargas sin corte del último paso. El comandopm2 startupmuestra una línea para copiar y pegar que crea el servicio systemd. Ejecútela con permisos de administrador. Verifique que la aplicación está en línea:pm2 statusdebe mostrar el estadoonline.Configurar Nginx como reverse proxy
Cree
/etc/nginx/sites-available/nuxt-app:server { listen 80; server_name sudominio.com www.sudominio.com; location /_nuxt/ { root /srv/nuxt-app/.output/public; expires 1y; add_header Cache-Control "public, immutable"; } location /favicon.ico { root /srv/nuxt-app/.output/public; } location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; 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; proxy_cache_bypass $http_upgrade; } }Active el sitio y pruebe la configuración:
ln -s /etc/nginx/sites-available/nuxt-app /etc/nginx/sites-enabled/ && nginx -t && systemctl reload nginx.Activar HTTPS con Let's Encrypt
Instale Certbot y el plugin de Nginx, luego genere el certificado:
apt install certbot python3-certbot-nginx certbot --nginx -d sudominio.com -d www.sudominio.comCertbot modifica automáticamente el bloque Nginx para escuchar en el puerto 443 y forzar la redirección HTTP → HTTPS. Verifique que
X-Forwarded-Protose transmite correctamente a Nitro — sin ello,useRequestHeaders()en el lado del servidor y las redirecciones basadas en el protocolo pueden comportarse incorrectamente. La renovación automática la gestiona el timer systemd de Certbot, comprobable consystemctl status certbot.timer.Actualizar sin interrupción del servicio
Para una actualización, el reload de PM2 (no el restart) garantiza la continuidad. Agrupe los comandos en un script
/srv/nuxt-app/deploy.sh, queset -edetiene en el primer error, antes de cualquier recarga:#!/bin/bash set -e cd /srv/nuxt-app git pull origin main npm ci npm run build pm2 reload nuxt-appHágalo ejecutable (
chmod +x deploy.sh) y llámelo desde su pipeline de CI/CD por SSH. En modo cluster, PM2 sustituye las instancias una a una: la nueva versión toma el relevo antes de que se detenga la antigua. Para un rollback, vuelva al commit anterior, reconstruya y recargue. Si el build se hace en CI, transfiera únicamente la carpeta.outputpor rsync y recargue sin reconstruir en el servidor.
Variables de entorno: públicas, privadas y recarga
Nuxt organiza su configuración de ejecución en dos niveles. Las claves situadas bajo runtimeConfig.public se envían al navegador y siguen siendo legibles en el servidor: URL pública de la API, identificador de analítica. Las demás claves de runtimeConfig nunca salen del servidor: claves secretas, acceso a la base de datos.
Al arrancar, una variable de entorno sustituye a la clave correspondiente si sigue la convención NUXT_<NOMBRE>, o NUXT_PUBLIC_<NOMBRE> para una clave pública: NUXT_PUBLIC_API_BASE reemplaza runtimeConfig.public.apiBase. Un valor leído de otra forma, por ejemplo process.env.OTRA_VARIABLE escrito en nuxt.config.ts, solo se toma durante el build y queda congelado en el artefacto.
En la práctica, cambiar una URL de API o una clave no exige reconstruir: se modifica el .env en el servidor y se recarga PM2 (pm2 reload nuxt-app). La misma carpeta .output puede así servir preproducción y producción con dos archivos de entorno distintos. La única condición es que el proceso cargue ese archivo, ya que Nuxt deja de leer .env una vez construida la aplicación: de ahí la opción --env-file que se pasa a Node al arrancar con PM2.
Optimizar el renderizado con routeRules
Una de las fortalezas de Nuxt en un VPS es la granularidad de las estrategias de renderizado página a página, sin cambiar de infraestructura. En nuxt.config.ts, las routeRules permiten mezclar SSR, SSG, ISR y SWR en un único despliegue Nitro.
Las páginas estáticas (blog, avisos legales, landing pages) pueden pregenerarse en el build con prerender: true: Nitro las sirve desde disco, sin Node. Las páginas semi-dinámicas (listas de productos, dashboards públicos) se benefician de swr: 60 para una caché de un minuto invalidada en segundo plano. El SSR completo queda reservado para las páginas personalizadas que dependen de la sesión o del perfil del usuario.
Este enfoque reduce la carga sobre Node sin necesitar un CDN de pago o una capa de caché externa — su Nginx almacena lo que Nitro pregenera.
Nitro server vs Docker vs Vercel/Netlify para alojar Nuxt
Desplace la tabla
| Enfoque | Ventajas | Limitaciones |
|---|---|---|
| Nitro `node-server` + PM2 | Simple, ligero, recarga sin downtime, costo fijo VPS | Supervisión a gestionar, servidor único sin alta disponibilidad nativa |
| Docker en VPS | Aislamiento, reproducibilidad, rollback por imagen, compose multi-app | Imagen a construir y almacenar, memoria adicional por contenedor |
| Vercel / Netlify | Configuración cero, CDN edge global, previews por PR | Cuotas de funciones, costo variable, sin control sobre la infraestructura |
| Kamal (despliegue zero-downtime) | Orquesta Docker y kamal-proxy, despliegue con un solo comando (`kamal deploy`) | Herramienta Ruby (gem o imagen Docker), configuración inicial más larga, pensado para equipos |
Registros: leerlos, rotarlos y cruzarlos con Nginx
PM2 centraliza la salida de la aplicación: pm2 logs nuxt-app sigue el flujo en directo (salida estándar y errores) y pm2 logs nuxt-app --lines 100 muestra las últimas líneas. Los archivos se escriben en ~/.pm2/logs/, un -out.log y un -error.log por aplicación, con el número de instancia como sufijo en modo cluster.
Estos archivos no rotan por sí solos. El módulo pm2-logrotate los rota al superar los 10 MB y conserva 30 por defecto; ajuste estos umbrales al tamaño de su disco:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7Cuando el navegador recibe un 502 o un 504, mire primero del lado de Nginx: /var/log/nginx/error.log indica si no pudo alcanzar 127.0.0.1:3000 (proceso detenido, puerto erróneo) o si Nitro tardó demasiado en responder.
Resolución de problemas: errores comunes en producción
Error: listen EADDRINUSE: address already in use :::3000
Un proceso ya ocupa el puerto 3000. Identifíquelo con ss -tlnp | grep 3000 y detenga la instancia PM2 huérfana: pm2 delete nuxt-app seguido de un reinicio limpio. Evite lanzar múltiples instancias sin un load balancer — configure el cluster_mode de PM2.
502 Bad Gateway en Nginx
Nitro no está arrancado o escucha en el puerto incorrecto. Compruebe pm2 status y revise los logs: pm2 logs nuxt-app --lines 50. Verifique también que la directiva proxy_pass en Nginx apunta a http://127.0.0.1:3000 (no a localhost que puede resolver a IPv6 si el proceso solo escucha en IPv4).
useRuntimeConfig() devuelve undefined
Las variables NUXT_* solo se leen cuando arranca el proceso Nitro: tras modificar .env, recargue con pm2 reload nuxt-app. Compruebe que el archivo se carga realmente mediante --node-args="--env-file=…" y no exportado en el shell actual, que desaparece con la sesión SSH. Si el valor falta solo en el navegador, la clave debe declararse bajo runtimeConfig.public y la variable llevar el prefijo NUXT_PUBLIC_.
Las rutas server/api devuelven 404 en producción
Compruebe que su build incluye la carpeta server/ en .output. Un nitro.preset mal configurado (por ejemplo static en vez de node-server) genera un sitio estático sin servidor. Reconstruya con npm run build tras verificar nuxt.config.ts.
X-Forwarded-Proto ignorado, bucle de redirección
Si Nuxt redirige HTTP → HTTPS en bucle, significa que el middleware de redirección ve http pese al HTTPS del cliente. Asegúrese de que Nginx transmite proxy_set_header X-Forwarded-Proto $scheme y que su nuxt.config.ts no fuerza la redirección HTTPS dos veces junto a Certbot.
Supervisión y métricas con PM2
Más allá del simple pm2 status, PM2 expone un panel en tiempo real con pm2 monit: CPU, memoria, reinicios y logs de procesos. Para una aplicación Nuxt bajo carga, active el modo cluster para usar todos los vCPU disponibles:
pm2 start .output/server/index.mjs --name nuxt-app -i maxPM2 distribuye automáticamente las peticiones entre los workers. Exporte estas métricas a Prometheus y Grafana para recibir alertas sobre reinicios inesperados — señal de un bug o fuga de memoria a corregir antes de que afecte a los usuarios.
La documentación oficial
Para la configuración avanzada y los cambios de versión, consulte la documentación oficial de Nuxt (routeRules, runtimeConfig, módulos), la página de Node.js de Nitro para las variables NITRO_PORT y NITRO_HOST, y la documentación del modo cluster de PM2. Esta guía cubre la puesta en línea en un VPS; la documentación del editor sigue siendo la referencia para los ajustes finos.