Deploy Next.js 15 & Express Backend to cPanel: A Battle-Tested Guide
What I learned from a frustrating but educational deployment journey
Deploying modern JavaScript frameworks to traditional cPanel hosting can feel like fitting a square peg into a round hole. After hours of debugging EAGAIN errors, CORS issues, and resource limits, I finally got my Next.js 15 frontend and Express.js backend running smoothly on Jagoan Hosting's cPanel. Here's everything I learned.
The Stack
- Frontend: Next.js 15, React 19, Material-UI v7
- Backend: Express.js, TypeScript, Sequelize
- Hosting: Jagoan Hosting Cloud NextGen (cPanel with Node.js support)
- Domains:
- Frontend:
dapurenakeeco.com - Backend API:
api.dapurenakeeco.com
- Frontend:
Part 1: Deploying the Express Backend
The backend was relatively straightforward. Here's the process:
Step 1: Build Locally
cd restaurant-backend
npm run build
This compiles TypeScript to JavaScript in the dist/ folder.
Step 2: Prepare Files for Upload
Create a zip containing:
dist/(compiled code)package.jsonpackage-lock.json.env(with production values)
DO NOT include node_modules - we'll install on the server.
Step 3: Setup Node.js App in cPanel
- Go to cPanel → Setup Node.js App
- Click Create Application
- Configure:
- Node.js Version: 20.x
- Application Mode: Production
- Application Root:
restaurant-backend - Application URL:
api.dapurenakeeco.com - Startup File:
dist/app.js
Step 4: Upload & Install
- Upload your zip to the
restaurant-backendfolder - Extract it
- Click Run NPM Install in the Node.js App interface
- Click Start App
CORS Configuration (Critical!)
Make sure your backend allows requests from your frontend domain:
// app.ts
app.use(
cors({
origin: [
"https://dapurenakeeco.com",
"http://dapurenakeeco.com",
"http://localhost:3000",
],
credentials: true,
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
}),
);
Pro tip: Double-check your domain spelling! I wasted time because of a typo (dapurenakeco vs dapurenakeeco).
Part 2: Deploying Next.js 15 (The Hard Part)
This is where things got interesting. Modern Next.js is resource-hungry, and shared hosting has strict limits.
The Problem: EAGAIN Errors
When I tried npm install on the server, I got:
Error: spawn npm EAGAIN
errno: -11
code: 'EAGAIN'
syscall: 'spawn npm'
Translation: The server hit its process limit (nProc). Shared hosting typically limits you to 20-50 concurrent processes, and npm install for a Next.js project spawns way more than that.
The Solution: Custom Server + Local Dependencies
After many failed attempts, I found a working approach from Jagoan Hosting's own tutorial.
Step 1: Create a Custom Server
Create server.js in your project root:
const { createServer } = require("http");
const { parse } = require("url");
const next = require("next");
const dev = process.env.NODE_ENV !== "production";
const hostname = "localhost";
const port = process.env.PORT || 8080;
const app = next({ dev, hostname, port });
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 occurred handling", req.url, err);
res.statusCode = 500;
res.end("internal server error");
}
}).listen(port, (err) => {
if (err) throw err;
console.log(`> Ready on http://${hostname}:${port}`);
});
});
Step 2: Update package.json
{
"scripts": {
"start": "NODE_ENV=production node server.js",
"build": "next build"
}
}
Step 3: Optimize for Shared Hosting
Add this to next.config.js to prevent resource exhaustion:
const nextConfig = {
experimental: {
workerThreads: false,
cpus: 1,
},
};
Step 4: Build Locally
npm run build
Step 5: Prepare Upload Package
Zip these files/folders:
.next/(build output)pages/public/src/server.jspackage.jsonpackage-lock.jsonnext.config.jstsconfig.json.env.local
DO NOT include node_modules - we'll run npm install on the server.
Step 6: Upload & Configure in cPanel
This is the most critical part. Follow these steps carefully.
6.1 Create the Node.js Application
- Log in to cPanel
- Go to Setup Node.js App
- Click Create Application
- Fill in the settings:
| Setting | Value |
| Node.js Version | 20.x (or latest LTS) |
| Application Mode | Production |
| Application Root | e-menu-fe (your folder name) |
| Application URL | dapurenakeeco.com (your domain) |
| Application Startup File | server.js |
- Click Create
6.2 Upload Your Files
- Go to File Manager in cPanel
- Navigate to
e-menu-fefolder - Delete any default files (like
app.js) - Click Upload and select your zip file
- After upload, right-click the zip → Extract
- Verify your folder structure looks like:
e-menu-fe/
├── .next/
├── pages/
├── public/
├── src/
├── server.js
├── package.json
├── package-lock.json
├── next.config.js
└── tsconfig.json
6.3 Set Environment Variables
- Go back to Setup Node.js App
- Click the pencil icon (Edit) on your app
- Scroll to Environment Variables
- Click Add Variable and add:
| Name | Value |
| PORT | 8080 |
| NEXT_PUBLIC_API_URL | https://api.dapurenakeeco.com |
- Click Save
6.4 Start the Application
- In Setup Node.js App, find your app
- Click Run NPM Install and wait for it to complete
- Click Start App (or Restart if already running)
- Wait 10-30 seconds for Next.js to initialize
- Visit your domain to verify it's working
Common Issues & Solutions
Issue: "Couldn't find any pages or app directory"
Cause: Missing source files on server.
Fix: Make sure you uploaded pages/, src/, and tsconfig.json.
Issue: "ENOENT: no such file... required-server-files.json"
Cause: Incomplete .next folder.
Fix: Rebuild locally with npm run build and re-upload the entire .next folder.
Issue: "fork: Resource temporarily unavailable"
Cause: Too many processes running. Your account is locked.
Fix: Contact hosting support to kill stuck processes, or wait a few hours. Then upload with node_modules included to avoid server-side installs.
Issue: CORS errors
Cause: Backend doesn't allow frontend origin.
Fix:
- Check domain spelling in CORS config
- Ensure both HTTP and HTTPS variants are listed
- Rebuild and re-upload backend
Issue: Non-standard NODE_ENV warning
Cause: cPanel sets a weird NODE_ENV value.
Fix: Set NODE_ENV=production explicitly in your start script (already done in our package.json).
Key Takeaways
Shared hosting has limits - Be aware of resource limits, but modern plans can handle npm install.
Use custom server.js - The default Next.js startup doesn't play well with cPanel.
cpus: 1 saves lives - Limit worker threads to avoid hitting process limits.
Double-check your domains - Typos in CORS configs cause silent failures.
Final Thoughts
Would I recommend cPanel for Next.js 15? Honestly, for small projects with limited traffic, it works. But if you're building something serious, consider:
- Vercel (made for Next.js)
- Railway/Render (easy Node.js deployment)
- VPS with Docker (full control)