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.
What you need
Nuxt on VPS: SSR vs Static Generation
Before deploying, decide which approach suits your use case:
nuxt buildUse 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.
nuxt generateUse 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:
| Spec | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2 vCPU |
| RAM | 1 GB | 2 GB |
| Storage | 20 GB SSD | 40 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:
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:
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:
Set an A record (yourdomain.com) and CNAME (www) pointing to your server's IP.
Certbot needs port 80 for domain validation. Allow it: sudo ufw allow 80
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
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.
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.
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.
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.
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:
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:
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.
Troubleshooting Nuxt deployments
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
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.
Check PM2 logs: pm2 logs — verify NODE_ENV is set to production and all required env vars are defined.
Ensure Nginx is correctly proxying to your Nuxt app and the baseURL in nuxt.config.ts matches your deployment path.
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