Why host Nuxt on a VPS?
Shared hosting platforms do not natively support Node.js, making it impossible to self-host a Nuxt application without a VPS or dedicated server. A VPS lets you choose the Node.js version you want, configure a reverse proxy (Nginx or Caddy) on any port, and manage your own TLS certificates — all without artificial CPU or memory limits. This is the ideal solution for independent developers and SMBs who want full control over their stack.
Unlike PaaS or serverless solutions, a VPS does not bill per execution and does not cap response times: an API call taking well below one second incurs no extra cost. You also keep full control over HTTP headers, edge caching and TLS configuration — three key levers for SEO and real-world performance.
SSR, SSG or SPA: which rendering mode for your Nuxt project?
Scroll the table
| Mode | Rendering | Node process required | Best for |
|---|---|---|---|
| SSR (ssr: true) | On request, server-side | Yes — permanent PM2 | Dynamic app, SEO, personalised content |
| SSG (nuxt generate) | At build time, static HTML | No — nginx is enough | Blog, landing page, rarely updated content |
| SPA (ssr: false) | In the browser | No — nginx is enough | Internal dashboard, auth-gated app |
| Hybrid (routeRules) | Per route: SSR + SSG mixed | Yes — Nitro server | App with static pages and API routes |
What you can do with Nuxt on a VPS
A ServOrbit VPS with the preconfigured Nuxt template opens four dimensions that shared hosts or PaaS platforms close off:
Concrete capabilities on VPS
- Serve a Nuxt application in SSR mode for optimal SEO and reduced load times — PM2 keeps the Nitro process alive and automatically restarts it after a crash or server reboot
- Deploy a static site generated with
nuxt generateand serve it through Nginx without a permanent Node.js process — ideal for a blog or landing page with infrequent updates - Host multiple Nuxt projects on the same VPS using a reverse proxy and Nginx Virtual Hosts — each application runs on its own internal port (3000, 3001…)
- Configure sensitive environment variables (API keys, DB connections) directly on the server in
/etc/environmentor via an.envfile outside the Git repository - Enable Nitro API routes (
server/api/) to expose a lightweight backend layer from the same Node.js process, with no separate Express server - Automate deployments with a PM2 ecosystem file, GitHub webhooks or a CI/CD pipeline pointing to your VPS via SSH
- Enable HTTPS automatically with Let's Encrypt via Certbot to secure your users at no certificate cost
Install and deploy Nuxt on your ServOrbit VPS
Order a ServOrbit Cloud VPS
Go to the
/vps-cloudpage and choose the plan that fits your project. For a Nuxt SSR application, plan for at least 1 vCPU and 1 GB RAM; 2 GB comfortably absorbs the Nitro process, a reverse proxy and compilation spikes during deployments. If you host multiple Nuxt applications, start with 2 vCPU / 4 GB. Complete the order and access your client portal once the VPS is delivered — typically in under a minute.Install Nuxt from the Marketplace
Log in to your ServOrbit client portal, navigate to the Marketplace section from your VPS dashboard, then search for Nuxt. Click the card, confirm the installation. The script automatically configures Node.js LTS, PM2 as the process manager, Nginx as a reverse proxy on port 80 (and 443 if a domain is provided). The operation takes under two minutes. At the end, your VPS already exposes a sample Nuxt server accessible via the VPS IP.
Deploy your application and configure environment variables
Connect via SSH (
ssh root@<VPS-IP>), then clone your repository into the prepared directory:git clone https://github.com/your-org/your-app.git /var/www/nuxt-app cd /var/www/nuxt-app npm installThen create a
.envfile outside your Git repository with your runtime variables:NUXT_PUBLIC_API_BASE=https://api.your-domain.com NUXT_SECRET_KEY=your-private-key DATABASE_URL=postgres://user:pass@localhost/mydbNuxt 3 reads variables prefixed with
NUXT_viauseRuntimeConfig()—NUXT_PUBLIC_*variables are exposed client-side, others remain server-only. Never commit the.envfile: add it to.gitignorefrom the start.Build the application and launch it with PM2
Compile the application for production:
npm run buildNuxt 3 generates the
.output/folder containing the Nitro server. Launch it with PM2 in cluster mode to use all CPU cores:pm2 start .output/server/index.mjs -i max --name nuxt-app pm2 save pm2 startupThe
pm2 startupcommand generates the systemd line to execute so that PM2 restarts automatically on server reboot. Check the status:pm2 status— instances should beonline. For zero-downtime reloads during deployments:pm2 reload nuxt-app.Configure Nginx as a reverse proxy and point your domain
The Marketplace template installs a ready-to-use Nginx Virtual Host in
/etc/nginx/sites-available/nuxt-app. Edit it to set your domain name and enable static asset caching:server { server_name your-domain.com; 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_cache_bypass $http_upgrade; } location /_nuxt/ { proxy_pass http://127.0.0.1:3000; expires 1y; add_header Cache-Control "public, immutable"; } }Point your DNS A record to the VPS IP, then run Certbot to obtain a free TLS certificate:
certbot --nginx -d your-domain.com. Reload Nginx:nginx -s reload. Your Nuxt application is now available over HTTPS.Automate subsequent deployments
For updates, create a deployment script at
/var/www/nuxt-app/deploy.sh:#!/bin/bash set -e cd /var/www/nuxt-app git pull origin main npm install --production=false npm run build pm2 reload nuxt-app echo "Deployment complete"Make it executable (
chmod +x deploy.sh) and trigger it from your CI/CD pipeline via SSH:ssh root@<IP> /var/www/nuxt-app/deploy.sh. PM2reload(notrestart) ensures a smooth transition — PM2 waits for each new instance to be ready before cutting the old one.
Environment variables and Nuxt 3 runtime config
Nuxt 3 distinguishes two levels of variables via useRuntimeConfig():
- runtimeConfig.public.* → available both client-side and server-side (e.g. public API URL, tracking keys)
- runtimeConfig.* (non-public) → server only (e.g. secret keys, DB credentials)
Environment variables override them automatically at runtime following the NUXT_<NAME> convention (public: NUXT_PUBLIC_<NAME>). This means you do not need to rebuild the application to change an API URL or access key: simply modify the .env file on the server and reload PM2 (pm2 reload nuxt-app). This is particularly useful for multiple environments (staging, production) that share the same build artifact.
For critical secrets (DB tokens, encryption keys), prefer system-level variables in /etc/environment over an .env file in the application directory — they are loaded at the system level and are not exposed by a directory read.
Monitor logs and diagnose errors
PM2 centralises your Nuxt application logs in real time. Key commands:
pm2 logs nuxt-app # live stream (stdout + stderr)
pm2 logs nuxt-app --lines 100 # last 100 lines
pm2 monit # real-time CPU / memory dashboardLogs are persisted in ~/.pm2/logs/ — nuxt-app-out.log for standard output and nuxt-app-error.log for errors. Configure log rotation to avoid filling the disk:
pm2 install pm2-logrotate
pm2 set pm2-logrotate:max_size 50M
pm2 set pm2-logrotate:retain 7For Nginx errors (502, 503 when PM2 is offline), check /var/log/nginx/error.log. A persistent 502 error generally means the PM2 process is not running (pm2 status) or port 3000 is not reachable from the proxy.
Diagnosing common issues
Three situations come up frequently when deploying a Nuxt application on a VPS:
502 Bad Gateway error. Nginx cannot reach the Nitro process on port 3000. Common causes: pm2 start was never run or has stopped (pm2 status to check), port 3000 is blocked by the firewall (ufw allow 3000 temporarily to test), or the application crashed at startup (pm2 logs nuxt-app --lines 50 to read the last error).
Unresolved environment variables. useRuntimeConfig() returns undefined on the client side for a variable that should be public. Make sure the variable is prefixed with NUXT_PUBLIC_ in your .env file, and that you restarted PM2 after the change: pm2 reload nuxt-app. Environment variables are only read when the Nitro process starts, not in real time.
Blank page in production after nuxt generate. The static HTML renders but dynamic data is empty. This is not a server issue — it is a baseURL issue: on the server side during prerendering, a $fetch with a relative path has no origin. Pass the full URL of your API in NUXT_PUBLIC_API_BASE and use useAsyncData with useFetch by specifying this base explicitly in pages that require crawlable content.
Performance tip: PM2 cluster mode. The command pm2 start .output/server/index.mjs -i max --name nuxt-app starts as many instances as there are available CPU cores and automatically distributes the load between them. On a 2-vCPU VPS you get two active Nitro workers, doubling throughput on SSR routes — ideal for absorbing traffic spikes without upgrading your plan. Verify with pm2 status that all N instances are online.
Official documentation
For advanced configuration, tool-specific options and version changes, refer to the official Nuxt documentation. This guide covers going live on ServOrbit VPS; the publisher documentation remains the reference for fine-tuning (routeRules, Nitro presets, official modules).