¿Por qué autoalojar Nuxt en un VPS?
Con Nitro, Nuxt 3 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.
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 compartiendo un mismo VPS detrás de Nginx.
- 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.
Requisitos de hardware y software
Como todo framework moderno, el build de Nuxt consume memoria: apunte a 2 GB de RAM como mínimo (el build puede realizarse en CI si su VPS es más pequeño). En runtime, 1 vCPU y 1 GB bastan para el servidor Nitro bajo carga ligera; suba a 2 vCPU / 2 GB para aplicaciones con muchas rutas server/api concurrentes.
Instale Node.js 20 LTS mediante nvm o una imagen node:20-alpine, además de PM2 para la supervisión de procesos. Configure nitro: { preset: 'node-server' } en nuxt.config.ts. Se requiere un dominio apuntando al VPS para el certificado TLS. Ubuntu 22.04 o 24.04 LTS sigue siendo la base recomendada.
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 20 LTS connvm install 20 && nvm use 20 && nvm alias default 20, luego PM2 de forma global:npm install -g pm2.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=0.0.0.0 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).Arrancar el servidor Nitro con PM2
Inicie la aplicación y configure el arranque automático:
pm2 start /srv/nuxt-app/.output/server/index.mjs \ --name nuxt-app \ --env-file /srv/nuxt-app/.env pm2 save pm2 startupEl comando
pm2 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:
cd /srv/nuxt-app git pull npm ci npm run build pm2 reload nuxt-appPM2 lanza la nueva versión, espera a que responda y luego corta la antigua: cero downtime. 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.
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 + Traefik, git push es suficiente | Requiere Ruby, configuración inicial más larga, pensado para equipos |
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 en el servidor
Las variables NUXT_* solo se leen cuando arranca el proceso Nitro. Si modifica .env después del lanzamiento, recargue: pm2 reload nuxt-app. Asegúrese de usar --env-file al arrancar y no exportar las variables en el shell actual.
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. Combine PM2 con una herramienta como Grafana vía pm2-prometheus para alertas sobre reinicios inesperados — señal de un bug o fuga de memoria a corregir antes de que afecte a los usuarios.