Why Self-Host Nuxt on a VPS?
With Nitro, Nuxt 3 generates a portable build (preset: node-server) that starts as a simple Node server listening on a port. This portability is ideal for a VPS: you benefit from server-side rendering, server/api routes, and route caching without depending on a proprietary edge runtime. Self-hosting removes function quotas and lets you tune caching, compression, and the number of Node instances. For a production Vue 3 application with authentication, server API calls, and careful SEO, the VPS offers the stability of a persistent server and a fixed cost, with no per-invocation billing surprises.
Concrete Benefits of a Self-Hosted Nuxt
- Unlimited
server/apiserver routes, with no per-call billing. - SSR and Vue 3 hydration served from a persistent, stable Node server.
- Portable Nitro build (
node-server) easy to containerize and reproduce. - Route caching and
routeRulesrules (SSR, SSG, ISR) controlled on the server side. - Pooling of several Nuxt apps on a single VPS behind Nginx.
- Runtime environment variables (
runtimeConfig) managed directly on your own server.
Hardware and Software Prerequisites
As with any modern framework, the Nuxt build consumes memory: aim for 2 GB of RAM minimum (the build can be run in CI if your VPS is smaller). At runtime, 1 vCPU and 1 GB are enough for the Nitro server. Install Node.js 20 LTS via nvm or a node:20-alpine image, along with PM2 for supervision or Docker for isolation. Configure nitro: { preset: 'node-server' } in nuxt.config.ts. A domain pointed to the VPS is required for the TLS certificate. Ubuntu 22.04/24.04 LTS remains the recommended base.
Deploy Nuxt Step by Step
Configure the Project for Node
Set the Nitro node-server preset in nuxt.config.ts and declare your secrets in runtimeConfig. On the VPS side, install Node.js 20 LTS and PM2, then clone the repository and create the .env file.
Build the Application
Run npm ci, then npm run build. Nitro produces a self-contained .output folder containing server/index.mjs. This folder is all the VPS needs to serve the application in production.
Launch the Nitro Server
Start with pm2 start .output/server/index.mjs --name nuxt, setting PORT=3000 and NITRO_HOST=0.0.0.0 in the environment. Run pm2 save and pm2 startup to restart the service after a VPS reboot.
Place Nginx as a Reverse Proxy
Configure a server block with proxy_pass http://localhost:3000, forward Host and X-Forwarded-Proto, and serve /_nuxt/ (hashed assets) directly from .output/public with a long cache. Enable gzip or brotli.
Enable HTTPS
Generate a Let's Encrypt certificate with Certbot for yourdomain.com, force the HTTP-to-HTTPS redirect, and schedule automatic renewal. Verify that SSR correctly detects the secure protocol via the forwarded headers.
Automate Updates
Set up a git pull && npm ci && npm run build && pm2 reload nuxt script or a CI pipeline that pushes the .output folder. PM2's reload guarantees a deployment with no service interruption.
Leverage Nuxt's routeRules to mix rendering strategies page by page: prerender: true for static pages (blog, legal notices), swr or isr for semi-dynamic pages with caching, and full SSR for personalized pages. You reduce the load on Node and improve response time without changing infrastructure.