DEV Community

Bravian
Bravian

Posted on

How I Hosted Next.js on $5 Shared Hosting

I recently challenged myself on TikTok by making a post saying:

"I will build your website for free for a week."

It was a chaotic, fun, and educational week. But the biggest plot twist didn't come from the code, it came from the infrastructure.

I had a client with a "vibe coded" Next.js project (generated fully by AI tools). It had a lot of flaws, and I could only correct so much, but they just needed help with the hosting. I quickly agreed because hosting is my specialty and I thought this would be easy—at least until I heard the details. He had already paid for a year of budget shared hosting (cPanel) and insisted we use it.

This was honestly uncharted waters. I always thought it was not possible, and after I looked up "host Next.js on shared hosting," the internet—and every AI assistant I asked—gave me the same answer:

"Impossible. Next.js requires too much RAM. Shared hosting kills long-running processes. You need a VPS or Vercel."

As a DevOps engineer, I took that personally.

I dug deeper and found a forgotten hero in the cPanel stack: Phusion Passenger. It turns out, you can host Next.js on cheap shared hosting, and it works surprisingly well.

Below is exactly how I did it.


The Secret Sauce: Phusion Passenger

Standard shared hosting is great for PHP but hostile to Node.js apps that need to run permanently in the background. If you try to run:

npm start
Enter fullscreen mode Exit fullscreen mode

…the server's process monitor will eventually kill it for using too much memory.

However, most modern cPanel hosts use CloudLinux with Phusion Passenger.

Passenger acts as a bridge. Instead of your app running 24/7 consuming RAM, Passenger "wakes up" your app when a request comes in and handles traffic - kind of like serverless.

The trick is simple:

Build locally. Run only the production build on the server.


The Guide: Next.js on cPanel

Prerequisites

  • A Next.js project
  • cPanel access with Setup Node.js App enabled
  • Node.js installed locally

Step 1: Create a Custom Server

Since shared hosting can’t reliably run next start as a long-lived process, we need a custom Node.js server that Phusion Passenger can manage.

This doesn’t replace Next.js — it simply gives Passenger a single entry point it understands.

Create a file called server.js in your project root:

const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');

const dev = false;
const hostname = 'localhost';
const port = process.env.PORT || 3000;

const app = next({
  dev,
  hostname,
  port,
  conf: {
    compress: true,
    poweredByHeader: false,
    generateEtags: false
  }
});

const handle = app.getRequestHandler();

app.prepare().then(() => {
  createServer(async (req, res) => {
    try {
      const parsedUrl = parse(req.url, true);
      await handle(req, res, parsedUrl);
    } catch (err) {
      console.error('Error handling request:', err);
      res.statusCode = 500;
      res.end('Internal server error');
    }
  }).listen(port, () => {
    console.log(`> Ready on http://${hostname}:${port}`);
  });
});
Enter fullscreen mode Exit fullscreen mode

Step 2: Optimize Next.js for Production

Create or update next.config.mjs:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  poweredByHeader: false,
  compress: true,

  typescript: {
    ignoreBuildErrors: true,
  },

  images: {
    deviceSizes: [640, 750, 828, 1080, 1200],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    // unoptimized: true,
  },

  compiler: {
    removeConsole: process.env.NODE_ENV === 'production',
  },

  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.fallback = {
        fs: false,
        net: false,
        tls: false,
      };
    }
    return config;
  },
};

export default nextConfig;
Enter fullscreen mode Exit fullscreen mode

You can make any improvements you want to this for health checks or logging

Step 3: Build Locally (Very Important)

Do not build on the server.

npm run build
Enter fullscreen mode Exit fullscreen mode

Shared hosting RAM limits will almost certainly crash the process.


Step 4: Package the App

  1. Delete node_modules
  2. Zip the entire project folder
  3. Make sure the zip includes:
    • .next
    • public
    • server.js
    • config files

Step 5: Upload & Extract

  • Open File Manager
  • Navigate to /home/youruser/myapp
  • Delete default files
  • Upload your zip
  • Extract it into folder myapp

Step 6: Setup Node.js in cPanel

  1. Open Setup Node.js App
  2. Click Create Application
  3. Configure:
    • Node Version: match local
    • Mode: Production
    • Application Root: myapp
    • Application URL: leave empty for default or add a specific one
    • Startup File: server.js
    • Add any environment variables if needed

Image showing the screen to expect when creating node.js application

Step 7: Install Dependencies

Back in Setup Node.js App:

  • Click Run NPM Install

If that fails:

npm install
Enter fullscreen mode Exit fullscreen mode

Run it inside the virtual environment via cPanel Terminal.


Step 8: Go Live

  1. Restart the Node.js app
  2. Open public_html
  3. Confirm that htaccess is created like this, it should have any environment variables you added.

Image showing my htaccess that was automatically generated

  1. Delete any default index.html
  2. Visit your domain

First load may take 10–15 seconds (cold start). After that, it's smooth.


Conclusion

Is this better than Vercel? No.

Is it production-grade for huge traffic? Also no.

But for:

  • Freelancers
  • Budget clients
  • Legacy hosting situations
  • Simple applications probably with few users

…it works - and it works well.

Sometimes, the "impossible" is just an undocumented feature and hopefully this documents it for the next person.

Top comments (0)