DeployWise
HomeGuidesDeploy Nuxt.js to VPS
Nuxt 3VPSSSRPM2Nginx

Deploy Nuxt.js to VPS: Step-by-Step Guide

Deploy a production-ready Nuxt 3 application to any Ubuntu VPS using DeployWise — covers SSR and static generation, Nitro configuration, PM2 process management, Nginx reverse proxy and free SSL certificates. No DevOps experience required.

12 min read
Updated 2026

What you need

A VPS running Ubuntu 20.04 or later (DigitalOcean, Hetzner, Vultr, etc.)
SSH access — either password or private key
A GitHub account with your Nuxt 3 repo
A domain name (optional, but recommended for SSL)

Nuxt on VPS: SSR vs Static Generation

Before deploying, decide which approach suits your use case:

Server-Side Rendering (SSR)
nuxt build

Use when: Your app has dynamic content, user-specific data, or real-time updates. The server renders HTML for every request.

Benefits: SEO-friendly, always up-to-date content, great for APIs and user interactions.

Static Generation (Pre-rendering)
nuxt generate

Use when: Your app is content-heavy (blog, docs) with mostly static pages. All HTML is pre-built.

Benefits: Ultra-fast load times, works without Node.js, can be served from CDN.

For this guide, we'll cover SSR (nuxt build) since it's the most common deployment scenario. Adjust the build command if you prefer static generation.

Nuxt 3 Production Setup

Your nuxt.config.ts should be optimized for production:

export default defineNuxtConfig({
  nitro: {
    presets: ['node-server'], // Not 'nodeserver'
    minify: true,
    timing: false,
  },
  ssr: true, // Enable SSR
  routeRules: {
    '/api/**': { cache: { maxAge: 60 } },
  },
  // Use relative paths for deployment
  app: {
    baseURL: '/',
  },
})

Key settings: ssr: true for server rendering, nitro.presets: ['node-server'] for Node.js VPS deployment, and minify: true for production.

1Set up your VPS

Start with a fresh Ubuntu 22.04 LTS droplet/instance. Make sure SSH is accessible on port 22 (or a custom port). You'll need either a root password or an SSH private key.

Recommended minimum specs for a single Nuxt.js app:

SpecMinimumRecommended
CPU1 vCPU2 vCPU
RAM1 GB2 GB
Storage20 GB SSD40 GB SSD
Monthly cost~$5/mo~$10/mo

2Add your server to DeployWise

Open DeployWise dashboard and sign in with GitHub. Then navigate to Servers → Add Server and fill in:

NameAny label (e.g. "Production VPS")
HostYour server's IP or hostname
Port22 (or your custom SSH port)
Usernameroot or your sudo user
AuthPassword or paste your private key

Click Test Connection. DeployWise will SSH into your server to verify connectivity. If the test passes, save the server.

3Create your Nuxt project

Go to Projects → New Project. Connect your GitHub account if you haven't already, then configure:

RepositorySelect your Nuxt 3 GitHub repo
Branchmain or production — whichever you deploy from
Build commandnpm run build (Nuxt 3 standard, can leave blank)
Start commandnode .output/server/index.mjs — required for SSR apps
Port3000 (Nuxt default) or your custom port
DomainOptional — your custom domain for Nginx + SSL setup
Auto DeployEnable to trigger deploys on every git push

Important: For SSR, the start command is node .output/server/index.mjs. This runs the Nitro server that was built by npm run build.

4PM2 Configuration for Nuxt

DeployWise automatically configures PM2, but here's what it does under the hood. PM2 ensures your Nuxt app restarts on crashes and boots on server reboot:

module.exports = {
  apps: [
    {
      name: 'nuxt-app',
      script: './.output/server/index.mjs',
      exec_mode: 'cluster',
      instances: 2, // Or 'max' for all CPU cores
      env: {
        NODE_ENV: 'production',
        HOST: '0.0.0.0',
        PORT: 3000,
      },
      error_file: './logs/err.log',
      out_file: './logs/out.log',
      merge_logs: true,
      autorestart: true,
      watch: false,
      max_memory_restart: '300M',
    },
  ],
};

Key points: The exec_mode: 'cluster' spreads your app across multiple CPU cores. max_memory_restart prevents memory leaks. DeployWise creates and manages this for you.

5Nginx Reverse Proxy Setup

Nginx sits in front of your Nuxt app, handling SSL, compression, and static file serving. DeployWise configures this automatically:

upstream nuxt_app {
  server 127.0.0.1:3000;
}

server {
  listen 80;
  listen [::]:80;
  server_name yourdomain.com;

  # Redirect HTTP to HTTPS
  return 301 https://$server_name$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name yourdomain.com;

  # SSL certificates (Let's Encrypt)
  ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

  # Gzip compression
  gzip on;
  gzip_types text/plain text/css application/javascript;

  location / {
    proxy_pass http://nuxt_app;
    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;
    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;
  }
}

This configuration redirects HTTP to HTTPS, serves static assets efficiently, and proxies dynamic requests to your Nuxt app on port 3000.

SSL Certificates with Let's Encrypt

DeployWise uses Certbot to issue and auto-renew free SSL certificates from Let's Encrypt. To enable:

1
Point your domain to your VPS

Set an A record (yourdomain.com) and CNAME (www) pointing to your server's IP.

2
Open port 80 (HTTP)

Certbot needs port 80 for domain validation. Allow it: sudo ufw allow 80

3
Configure domain in DeployWise

Enter your domain name when creating the project. DeployWise will request the certificate during deployment.

Certificates auto-renew every 90 days. Certbot runs as a systemd timer on your server.

Common Nuxt Deployment Mistakes

Wrong Nitro preset

Problem: Using 'nodeserver' instead of 'node-server' in nuxt.config.ts

Solution: Ensure: nitro: { presets: ['node-server'] } — this is the correct preset for VPS deployment with Node.js.

Missing .output directory

Problem: Deployment tries to start app before build completes, or .output is in .gitignore

Solution: Never commit .output to git — it's generated during build. DeployWise handles this automatically.

Environment variables not set

Problem: App builds successfully but crashes at runtime with undefined variables

Solution: Set NUXT_PUBLIC_* and private vars in DeployWise project settings before deployment. Restart PM2 after changes.

Incorrect start command

Problem: Using npm start instead of node .output/server/index.mjs

Solution: The start command must point to the compiled Nitro server directly, not a package.json script.

Build runs out of memory

Problem: nuxt build fails with 'FATAL ERROR: JavaScript heap out of memory'

Solution: Increase Node memory: NODE_OPTIONS=--max-old-space-size=2048 npm run build — or add swap space to your VPS.

6Deploy your Nuxt app

Click Deploy Now. DeployWise will execute the following steps on your server automatically:

1.Install Node.js via NVM (if not present)
2.Install PM2 globally
3.Clone your repository via Git
4.Run npm ci to install dependencies
5.Run npm run build to compile Nuxt + Nitro
6.Set environment variables from project settings
7.Start/restart the app with PM2 using ecosystem.config.js
8.Configure Nginx reverse proxy
9.Issue Let's Encrypt SSL certificate (if domain configured)

The entire process typically takes 45–120 seconds (depending on build time and dependencies). You can watch every step live in the deployment log panel.

What happens after deployment

Your Nuxt app is now:

Running under PM2 in cluster mode — restarts automatically if it crashes
Proxied through Nginx — handles HTTP/HTTPS, gzip compression, and static files
Protected with SSL — free Let's Encrypt certificate, auto-renewing
Accessible at your domain or via server IP
Pre-configured for hot reloads and auto-restarts on code changes

Future deploys are one click away. If something breaks, roll back to any previous commit from the deployment history.

How DeployWise Automates Nuxt Deployment

Manually deploying Nuxt involves 20+ manual steps: SSH setup, dependency installation, build configuration, PM2 ecosystem files, Nginx config generation, SSL certificate management, and environment variable handling. DeployWise does all of this automatically.

Server provisioning
Manual: 30 minDeployWise: Seconds
Build + PM2 setup
Manual: 15 minDeployWise: Automatic
Nginx + SSL
Manual: 20 minDeployWise: Automatic
Environment vars
Manual: 10 minDeployWise: Web UI
Monitoring + logs
Manual: Custom scriptsDeployWise: Dashboard
Rollback on failure
Manual: Git + manual restartDeployWise: One click

Troubleshooting Nuxt deployments

Build fails with 'out of memory'

The Nuxt build can require 1.5–2 GB RAM. If your VPS has only 1 GB, add swap space: sudo fallocate -l 1G /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile

App crashes after deployment with 'ENOENT: no such file or directory'

The start command may be incorrect. Verify the .output directory exists: ssh user@server 'ls -la /home/user/app/.output/server/' — ensure index.mjs is there.

Deployment succeeds but app won't start

Check PM2 logs: pm2 logs — verify NODE_ENV is set to production and all required env vars are defined.

Static assets (CSS, JS) return 404

Ensure Nginx is correctly proxying to your Nuxt app and the baseURL in nuxt.config.ts matches your deployment path.

SSL certificate fails to issue

Verify domain A record points to your VPS IP, port 80 is open (ufw allow 80), and no other service is using port 80.

Ready to deploy Nuxt?

Sign in with GitHub, add your VPS, and have your Nuxt app live in minutes — for free.

Open DeployWise Dashboard

Related guides