{"id":1836,"date":"2026-09-05T09:24:12","date_gmt":"2026-09-05T03:54:12","guid":{"rendered":"https:\/\/cpanelfree.com\/blog\/how-to-deploy-nextjs-app-ubuntu-vps-pm2-nginx\/"},"modified":"2026-09-05T12:57:31","modified_gmt":"2026-09-05T07:27:31","slug":"how-to-deploy-nextjs-app-ubuntu-vps-pm2-nginx","status":"publish","type":"post","link":"https:\/\/cpanelfree.com\/blog\/how-to-deploy-nextjs-app-ubuntu-vps-pm2-nginx\/","title":{"rendered":"How to Deploy Next.js 14 &amp; 15 App on Ubuntu VPS with PM2 and Nginx (Full SSR Guide)"},"content":{"rendered":"<h2>Why Self-Host Next.js on a Linux Cloud VPS?<\/h2>\n<p>Next.js has become the most popular React framework for modern full-stack web development, offering Server-Side Rendering (SSR), React Server Components (RSC), incremental static regeneration (ISR), and API route handlers. While managed hosting platforms like Vercel or Netlify offer simple git deployments, their bandwidth and serverless function execution costs escalate dramatically once your application receives millions of pageviews.<\/p>\n<p>Self-hosting Next.js on an affordable, high-performance Ubuntu Cloud VPS gives you total control over CPU resources, unmetered bandwidth, local caching engines (such as Redis), custom persistent file storage, and persistent database connections\u2014all at a fixed, predictable hosting cost.<\/p>\n<p>In this production-grade tutorial, we will configure Next.js 14\/15 on Ubuntu 24.04\/22.04 LTS using standalone build optimizations, PM2 process management in cluster mode, and an Nginx reverse proxy configured with HTTP\/2 and Let&#8217;s Encrypt SSL.<\/p>\n<h2>Step 1: Installing Node.js 20 LTS and Essential Build Tools<\/h2>\n<p>Next.js 14 and 15 recommend Node.js 18.17+ or Node.js 20 LTS. We install Node.js 20 LTS using the official NodeSource APT repository along with build essentials:<\/p>\n<pre><code># Update APT metadata and install prerequisite packages\nsudo apt update &amp;&amp; sudo apt install -y curl git build-essential nginx certbot python3-certbot-nginx\n\n# Add NodeSource Node.js 20 LTS repository\ncurl -fsSL https:\/\/deb.nodesource.com\/setup_20.x | sudo -E bash -\nsudo apt install -y nodejs\n\n# Verify Node.js and NPM versions\nnode -v\nnpm -v\n\n# Install PM2 globally\nsudo npm install -g pm2<\/code><\/pre>\n<h2>Step 2: Configuring Next.js Standalone Output Mode<\/h2>\n<p>By default, Next.js packages all development dependencies, which can result in a deployment folder larger than 1GB. Enabling <code>output: 'standalone'<\/code> tells Next.js to leverage tree-shaking and bundle only the exact production files needed to run the server, reducing memory footprint by up to 80%.<\/p>\n<p>In your Next.js project root, edit <code>next.config.mjs<\/code> (or <code>next.config.js<\/code>):<\/p>\n<pre><code>\/** @type {import('next').NextConfig} *\/\nconst nextConfig = {\n  output: 'standalone',\n  reactStrictMode: true,\n  poweredByHeader: false,\n  compress: true,\n  images: {\n    domains: ['cpanelfree.com'],\n    formats: ['image\/avif', 'image\/webp'],\n  },\n};\n\nexport default nextConfig;<\/code><\/pre>\n<h2>Step 3: Cloning, Installing, and Building on VPS<\/h2>\n<p>Clone your Next.js repository into <code>\/var\/www\/next-app<\/code> and build the production bundle:<\/p>\n<pre><code># Create webroot and clone repository\nsudo mkdir -p \/var\/www\/next-app\nsudo chown -R $USER:$USER \/var\/www\/next-app\ngit clone https:\/\/github.com\/your-username\/your-next-repo.git \/var\/www\/next-app\n\ncd \/var\/www\/next-app\n\n# Install dependencies and build\nnpm ci\nnpm run build\n\n# Copy static assets and public folder into standalone directory\ncp -r public .next\/standalone\/\ncp -r .next\/static .next\/standalone\/.next\/<\/code><\/pre>\n<h2>Step 4: Managing Next.js with PM2 Cluster Mode<\/h2>\n<p>To ensure high availability and automatic restarts upon crashes or server reboots, create a PM2 configuration file <code>ecosystem.config.cjs<\/code> in your project directory:<\/p>\n<pre><code>module.exports = {\n  apps: [\n    {\n      name: 'nextjs-production',\n      cwd: '\/var\/www\/next-app\/.next\/standalone',\n      script: 'server.js',\n      instances: 'max',\n      exec_mode: 'cluster',\n      env: {\n        NODE_ENV: 'production',\n        PORT: 3000,\n        HOSTNAME: '127.0.0.1',\n      },\n      max_memory_restart: '600M',\n      error_file: '\/var\/log\/nextjs-error.log',\n      out_file: '\/var\/log\/nextjs-out.log',\n    },\n  ],\n};<\/code><\/pre>\n<p>Launch the PM2 process and save the systemd auto-start configuration:<\/p>\n<pre><code># Start Next.js with PM2\npm2 start ecosystem.config.cjs\n\n# Save PM2 process list and configure auto-start on server boot\npm2 save\npm2 startup systemd<\/code><\/pre>\n<h2>Step 5: Nginx Reverse Proxy with WebSocket &amp; Caching Directives<\/h2>\n<p>Create an optimized Nginx server block at <code>\/etc\/nginx\/sites-available\/next.example.com<\/code>:<\/p>\n<pre><code>server {\n    listen 80;\n    server_name next.example.com;\n\n    # Gzip compression for high-speed delivery\n    gzip on;\n    gzip_proxied any;\n    gzip_types text\/plain text\/css application\/json application\/javascript text\/xml application\/xml application\/xml+rss text\/javascript image\/svg+xml;\n\n    location \/_next\/static\/ {\n        alias \/var\/www\/next-app\/.next\/static\/;\n        expires 365d;\n        access_log off;\n        add_header Cache-Control \"public, max-age=31536000, immutable\";\n    }\n\n    location \/ {\n        proxy_pass http:\/\/127.0.0.1:3000;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection 'upgrade';\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_cache_bypass $http_upgrade;\n        proxy_read_timeout 60s;\n    }\n}<\/code><\/pre>\n<p>Enable the site configuration and acquire an SSL certificate:<\/p>\n<pre><code>sudo ln -s \/etc\/nginx\/sites-available\/next.example.com \/etc\/nginx\/sites-enabled\/\nsudo nginx -t &amp;&amp; sudo systemctl reload nginx\nsudo certbot --nginx -d next.example.com<\/code><\/pre>\n<h2>Next.js VPS Deployment Architecture Matrix<\/h2>\n<table style=\"width: 100%;border-collapse: collapse;margin: 20px 0;border: 1px solid #334155\">\n<thead>\n<tr style=\"background-color: #0f172a;color: #38bdf8\">\n<th style=\"padding: 12px;border: 1px solid #334155\">Architecture Layer<\/th>\n<th style=\"padding: 12px;border: 1px solid #334155\">Technology<\/th>\n<th style=\"padding: 12px;border: 1px solid #334155\">Role &amp; Responsibility<\/th>\n<th style=\"padding: 12px;border: 1px solid #334155\">Performance Impact<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"background-color: #1e293b;color: #f8fafc\">\n<td style=\"padding: 10px;border: 1px solid #334155\"><strong>Edge Gateway<\/strong><\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Nginx Reverse Proxy<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">SSL Termination &amp; Static Assets Cache<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Sub-5ms static asset delivery<\/td>\n<\/tr>\n<tr style=\"background-color: #0f172a;color: #f8fafc\">\n<td style=\"padding: 10px;border: 1px solid #334155\"><strong>Process Supervisor<\/strong><\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">PM2 Cluster<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Multi-core load balancing &amp; self-healing<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">100% uptime with zero downtime reloads<\/td>\n<\/tr>\n<tr style=\"background-color: #1e293b;color: #f8fafc\">\n<td style=\"padding: 10px;border: 1px solid #334155\"><strong>Application Engine<\/strong><\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Next.js Standalone<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Server-Side Rendering &amp; React Server Components<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">80% reduced memory footprint<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>Frequently Asked Questions (FAQ)<\/h2>\n<h3>How do I update the Next.js app with zero downtime?<\/h3>\n<p>To deploy new updates without interrupting live visitor sessions, pull the latest code, run <code>npm run build<\/code>, copy the updated static files, and execute <code>pm2 reload nextjs-production<\/code>. PM2 will reload each cluster worker sequentially.<\/p>\n<h3>Can I connect Next.js to a local PostgreSQL database?<\/h3>\n<p>Yes. You can install PostgreSQL directly on the same Ubuntu VPS and connect using Prisma ORM or Drizzle via <code>postgresql:\/\/user:password@127.0.0.1:5432\/dbname<\/code> for ultra-low 0.1ms internal database latency.<\/p>\n<h2>Automating Continuous Zero-Downtime Deployments with GitHub Actions<\/h2>\n<p>To eliminate manual SSH logins and ensure that every merged pull request triggers an automated build, validation, and zero-downtime cluster reload, configure a GitHub Actions deployment workflow at <code>.github\/workflows\/deploy.yml<\/code>:<\/p>\n<pre><code>name: Next.js Zero-Downtime Deployment\n\non:\n  push:\n    branches: [main]\n\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n      - name: Setup Node.js\n        uses: actions\/setup-node@v4\n        with:\n          node-version: 20\n          cache: 'npm'\n      - run: npm ci\n      - run: npm run build\n      - name: Deploy to Cloud VPS via SSH\n        uses: appleboy\/ssh-action@v1.0.3\n        with:\n          host: ${{ secrets.SSH_HOST }}\n          username: ${{ secrets.SSH_USER }}\n          key: ${{ secrets.SSH_PRIVATE_KEY }}\n          script: |\n            cd \/var\/www\/next-app\n            git pull origin main\n            npm ci\n            npm run build\n            cp -r public .next\/standalone\/\n            cp -r .next\/static .next\/standalone\/.next\/\n            pm2 reload nextjs-production\n            echo \"Next.js cluster reloaded with zero downtime!\"<\/code><\/pre>\n<h2>Next.js VPS Troubleshooting &amp; Common Pitfalls<\/h2>\n<table style=\"width: 100%;border-collapse: collapse;margin: 20px 0;border: 1px solid #334155\">\n<thead>\n<tr style=\"background-color: #0f172a;color: #38bdf8\">\n<th style=\"padding: 12px;border: 1px solid #334155\">Symptom<\/th>\n<th style=\"padding: 12px;border: 1px solid #334155\">Cause<\/th>\n<th style=\"padding: 12px;border: 1px solid #334155\">Resolution<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"background-color: #1e293b;color: #f8fafc\">\n<td style=\"padding: 10px;border: 1px solid #334155\">Missing static CSS\/JS (404 on <code>\/_next\/static\/<\/code>)<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Static folder not copied to standalone directory<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Run <code>cp -r .next\/static .next\/standalone\/.next\/<\/code><\/td>\n<\/tr>\n<tr style=\"background-color: #0f172a;color: #f8fafc\">\n<td style=\"padding: 10px;border: 1px solid #334155\">High memory consumption \/ OOM crash<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">PM2 cluster running too many workers on small VPS<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Set <code>instances: 2<\/code> and configure a 2GB swapfile<\/td>\n<\/tr>\n<tr style=\"background-color: #1e293b;color: #f8fafc\">\n<td style=\"padding: 10px;border: 1px solid #334155\">Image optimization failing (500 error)<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Missing <code>sharp<\/code> image library in standalone mode<\/td>\n<td style=\"padding: 10px;border: 1px solid #334155\">Run <code>npm install sharp<\/code> in project root<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<div style=\"background-color: #0f172a;border-left: 4px solid #38bdf8;padding: 18px 24px;margin: 30px 0;border-radius: 8px\">\n<h3 style=\"color: #38bdf8;margin-top: 0\">Recommended Related Technical Guides<\/h3>\n<ul style=\"margin-bottom: 0;color: #cbd5e1\">\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-deploy-nodejs-express-app-linux-vps-nginx-pm2\/\" style=\"color: #38bdf8;text-decoration: underline\">Deploying Node.js &amp; Express Apps with PM2 and Nginx<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-automate-git-deployment-github-actions-vps\/\" style=\"color: #38bdf8;text-decoration: underline\">Automating Next.js Deployments with GitHub Actions CI\/CD<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-host-strapi-headless-cms-linux-vps-postgresql-pm2\/\" style=\"color: #38bdf8;text-decoration: underline\">Connecting Next.js to Self-Hosted Strapi Headless CMS<\/a><\/li>\n<\/ul>\n<\/div>\n<div style=\"background: linear-gradient(135deg, #0284c7 0%, #0369a1 100%);color: #ffffff;padding: 28px;border-radius: 12px;margin: 35px 0;text-align: center\">\n<h3 style=\"color: #ffffff;margin-top: 0;font-size: 22px\">Deploy Full-Stack Next.js Apps on CpanelFree High-Speed VPS<\/h3>\n<p style=\"color: #e0f2fe;font-size: 15px;max-width: 650px;margin: 0 auto 18px auto\">Scale your React and SSR web applications with high-speed NVMe cloud servers, unmetered bandwidth, and 100% free hosting options.<\/p>\n<p>  <a href=\"https:\/\/cpanelfree.com\/\" style=\"background-color: #ffffff;color: #0284c7;font-weight: 700;padding: 12px 28px;border-radius: 8px;text-decoration: none;display: inline-block\">Launch Free Cloud Hosting &rarr;<\/a>\n<\/div>\n<div style=\"border-left: 4px solid #38bdf8;border-radius: 8px;padding: 20px;margin: 30px 0\">\n<h3 style=\"margin-top: 0;color: #38bdf8;font-size: 18px;display: flex;align-items: center\">\n        <span style=\"margin-right: 8px\">\ud83d\udd17<\/span> Recommended Related Technical Guides:<br \/>\n    <\/h3>\n<ul style=\"margin: 10px 0 0 0;padding-left: 20px;line-height: 1.8\">\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-host-website-free-forever-guide\/\" style=\"color: #38bdf8;text-decoration: none;font-weight: 600\">How to Host a Website for Free Forever: Complete Beginner Guide (2026)<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/free-wordpress-hosting-softaculous-installer\/\" style=\"color: #38bdf8;text-decoration: none;font-weight: 600\">Top 5 Free WordPress Hosting Services with 1-Click Softaculous Installer<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-backup-linux-vps-to-cloud-storage-s3-rclone\/\" style=\"color: #38bdf8;text-decoration: none;font-weight: 600\">How to Automatically Backup Your Linux VPS to Cloud Storage (S3 \/ Rclone Guide)<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/blog\/how-to-scan-linux-server-malware-maldet-clamav\/\" style=\"color: #38bdf8;text-decoration: none;font-weight: 600\">How to Scan Linux Server for Malware and Backdoors (ClamAV &amp; Maldet Tutorial)<\/a><\/li>\n<li><a href=\"https:\/\/cpanelfree.com\/#plans\" style=\"color: #10b981;text-decoration: none;font-weight: 600\">Explore $0 Free cPanel Web Hosting Plans (NVMe SSD, AutoSSL)<\/a><\/li>\n<\/ul>\n<\/div>\n<div style=\"background: linear-gradient(135deg, rgba(6, 182, 212, 0.15) 0%, rgba(59, 130, 246, 0.15) 100%);border-radius: 12px;padding: 25px;margin: 30px 0;text-align: center\">\n<h3 style=\"color: #38bdf8;margin-top: 0;font-size: 20px\">Deploy Fast, Reliable Web Hosting on CpanelFree<\/h3>\n<p style=\"color: #94a3b8;font-size: 14px;line-height: 1.6;max-width: 600px;margin: 0 auto 15px\">\n        Get genuine cPanel control, unmetered NVMe SSD storage, and free AutoSSL at $0 cost forever.\n    <\/p>\n<p>    <a href=\"https:\/\/cpanelfree.com\/#plans\" style=\"display: inline-block;background: linear-gradient(135deg, #0284c7 0%, #0369a1 100%);color: #ffffff;padding: 10px 22px;border-radius: 6px;text-decoration: none;font-weight: bold;font-size: 14px\">Claim Free Hosting Account<\/a>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Why Self-Host Next.js on a Linux Cloud VPS? Next.js has become the most popular React framework for modern full-stack web development, offering Server-Side Rendering (SSR), React Server Components (RSC), incremental static regeneration (ISR), and API route handlers. While managed hosting platforms like Vercel or Netlify offer simple git deployments, their bandwidth and serverless function execution [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2490,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[166],"tags":[],"class_list":["post-1836","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-developer-stacks"],"_links":{"self":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts\/1836","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/comments?post=1836"}],"version-history":[{"count":2,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts\/1836\/revisions"}],"predecessor-version":[{"id":2290,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/posts\/1836\/revisions\/2290"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/media\/2490"}],"wp:attachment":[{"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/media?parent=1836"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/categories?post=1836"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cpanelfree.com\/blog\/wp-json\/wp\/v2\/tags?post=1836"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}