Deployment guide

Deploying Nuxt on a VPS: SSR, API Routes and Control

Deploy on a VPS Cloud →

Tutorial

Deploying Nuxt on a VPS: SSR, API Routes and Control

Development9 min read6 steps

Nuxt can serve a static site, an SSR application or an API layer via Nitro; the chosen rendering mode changes the server requirements. On a ServOrbit VPS, you control the Node runtime, environment variables and the proxy rather than being constrained by a closed platform. This guide covers the full path: rendering mode choice, build, PM2 service, domain, HTTPS, environment variables, HTTP cache and log monitoring.

Contents· Why host Nuxt on a VPS?1/9
  1. 01Why host Nuxt on a VPS?
  2. 02SSR, SSG or SPA: which rendering mode for your Nuxt project?
  3. 03What you can do with Nuxt on a VPS
  4. 04Concrete capabilities on VPS
  5. 05Install and deploy Nuxt on your ServOrbit VPS
  6. 06Environment variables and Nuxt 3 runtime config
  7. 07Monitor logs and diagnose errors
  8. 08Diagnosing common issues
  9. 09Official documentation

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

ModeRenderingNode process requiredBest for
SSR (ssr: true)On request, server-sideYes — permanent PM2Dynamic app, SEO, personalised content
SSG (nuxt generate)At build time, static HTMLNo — nginx is enoughBlog, landing page, rarely updated content
SPA (ssr: false)In the browserNo — nginx is enoughInternal dashboard, auth-gated app
Hybrid (routeRules)Per route: SSR + SSG mixedYes — Nitro serverApp 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 generate and 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/environment or via an .env file 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

  1. Order a ServOrbit Cloud VPS

    Go to the /vps-cloud page 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.

  2. 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.

  3. 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 install

    Then create a .env file 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/mydb

    Nuxt 3 reads variables prefixed with NUXT_ via useRuntimeConfig() — NUXT_PUBLIC_* variables are exposed client-side, others remain server-only. Never commit the .env file: add it to .gitignore from the start.

  4. Build the application and launch it with PM2

    Compile the application for production:

    npm run build

    Nuxt 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 startup

    The pm2 startup command generates the systemd line to execute so that PM2 restarts automatically on server reboot. Check the status: pm2 status — instances should be online. For zero-downtime reloads during deployments: pm2 reload nuxt-app.

  5. 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.

  6. 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. PM2 reload (not restart) 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 dashboard

Logs 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 7

For 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).

Deploy Nuxt in minutes on a ServOrbit Cloud VPS

Enjoy a high-availability Cloud VPS with 1-click Nuxt installation, preconfigured Node.js and responsive support — with no nasty surprises on the bill.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.

Message us on WhatsAppopens in a new tab