DEV Community

Boros Máté
Boros Máté

Posted on

How I Deployed Next.js to cPanel on Shared Hosting

Deploying Next.js to shared hosting can be tricky, and there's no universal solution. Every hosting platform has its differences, whether it's the operating system or available features. For example, some hosts allow SSH access, while others (like mine) don't. Because of that the solution that worked for me might not work for you
In my case, I had to rely solely on the cPanel dashboard to set up my Node.js app and troubleshoot issues.

I know, there are certainly better options out there but if you're on a budget or just want to experiment, shared hosting can be an accessible starting point.

Let me walk you through my experience.

The first step was modifying the Next.js configuration. I needed to set the export 'standalone' mode. This change accomplishes two important things:

  1. It eliminates the need to run any scripts in the cPanel build panel. I had an out-of-memory issue when I tried to run npm run build.
    cPanel run script image

  2. It generates a server.js file, which becomes our main entry point.

However, by default, not all required files are copied over.(Here's more info on that) So I also extended package.json with a postbuild script to copy the necessary files to the standalone folder:

   "build": "next build",
   "start": "next start",
   "lint": "next lint",
   "postbuild": "npm run copy-static",
   "copy-static": "mkdir -p .next/standalone/public && cp -r public/* .next/standalone/public/ && mkdir -p .next/standalone/.next/static && cp -r .next/static/* .next/standalone/.next/static/",
Enter fullscreen mode Exit fullscreen mode

Next, I set up a GitHub Action to automate the build and deployment process. This action handles two tasks:

  1. Runs the build for the Next.js application
  2. Transfers the built files to the correct location on the shared hosting server via FTP (I created a separate FTP user with limited access to the destination folder)
name: Deploy Next.js to cPanel via FTP

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Use Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Build Next.js app
        run: npm run build

      - name: Deploy to cPanel via FTP
        uses: SamKirkland/FTP-Deploy-Action@v4.3.4
        with:
          server: ftp.yourserver.com
          username: your-ftp@username
          password: ${{ secrets.FTP_PASSWORD }}
          local-dir: .next/standalone/
          protocol: ftp
          port: 21
Enter fullscreen mode Exit fullscreen mode

Note: If you're like me after every build and deployment you would need to manually restart the node js app on Cpanel.

After pressing the Run NPM Install button and starting the application, your app should be live at its Application URL. However, in my case, the images weren’t optimized and when I checked the stderr.log file in the application folder (You can use cPanel's File Manager to navigate there), I found this error:

⨯ Error: 'sharp' is required to be installed in standalone mode for image optimization to function correctly. Read more at: https://nextjs.org/docs/messages/sharp-missing-in-production
Enter fullscreen mode Exit fullscreen mode

At first, I was confused because I had already installed sharp, and I could see it in the symlinked node_modules folder.

To figure out what was happening, I temporarily modified the server.js file and added some code to collect debug information. Here's the snippet I used:

// Collect debug information
const debugInfo = {
  dirname: __dirname,
  cwd: process.cwd(),
  nodePath: process.env.NODE_PATH,
  path: process.env.PATH,
  sharpStatus: 'Not checked yet',
  sharpLocation: '',
};

// Try to load Sharp
try {
  const sharp = require('sharp');
  debugInfo.sharpStatus = `Loaded. Version: ${sharp.versions.sharp}`;
  debugInfo.sharpLocation = `Sharp location: ${require.resolve('sharp')}`;
} catch (error) {
  debugInfo.sharpStatus = `Error loading: ${error.message}`;
}

// Throw an error with the debug information
throw new Error(`Debug Info:
   __dirname: ${debugInfo.dirname}
   process.cwd(): ${debugInfo.cwd}
   NODE_PATH: ${debugInfo.nodePath}
   PATH: ${debugInfo.path}
   Sharp status: ${debugInfo.sharpStatus}
   Sharp location: ${debugInfo.sharpLocation}
`);
Enter fullscreen mode Exit fullscreen mode

This block of code gathers some information like NODE_PATH: which is an environment variable that tells Node.js where to look for modules in addition to the default node_modules directories and it can help you understand if there are any custom module resolution paths set up on your server.

Went back to stderr.log and I saw:

Error: Debug Info:
...
Sharp status: Error loading: Could not load the "sharp" module using the linux-x64 runtime
ERR_DLOPEN_FAILED: /lib64/libstdc++.so.6: version `CXXABI_1.3.8' not found (required by /home/ozuyuggp/nodevenv/yourdir/20/lib/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node)
Possible solutions:
- Ensure optional dependencies can be installed:
    npm install --include=optional sharp
- Ensure your package manager supports multi-platform installation:
    See https://sharp.pixelplumbing.com/install#cross-platform
- Add platform-specific dependencies:
    npm install --os=linux --cpu=x64 sharp
- Update your OS:
    Found glibc 2.17
    Requires glibc >=2.26
- Consult the installation documentation:
    See https://sharp.pixelplumbing.com/install
Enter fullscreen mode Exit fullscreen mode

After some digging, since I didn’t have control over the server environment, I would not be able to update packages, I decided the simplest solution was to roll back to an earlier version of sharp that had worked for others in similar situations.

I reverted to:

"sharp": "^0.32.6"
Enter fullscreen mode Exit fullscreen mode

Hit npm install, restart the app, and open the application URL again. Important note: Make sure to open or refresh the application URL every time. If not, server.js might not start, and you could miss new errors in the log file.

This time, the stderr.log showed:

Error: Debug Info:
__dirname: /home/yourserver/yourdirectory
process.cwd(): /home/yourserver/yourdirectory
NODE_PATH: /home/yourserver/nodevenv/yourdirectory/20/lib/node_modules:/opt/alt/alt-nodejs20/root/lib/node_modules:/opt/alt/alt-nodejs20/root/lib64/node_modules:
PATH: /home/yourserver/nodevenv/yourdirectory/20/bin:/opt/alt/alt-nodejs20/root/usr/bin:/home/yourserver/nodevenv/yourdirectory/20/lib/bin/:/usr/local/bin:/bin:/usr/bin
Sharp status: Loaded. Version: 0.32.6
Sharp location: Sharp. Location: /home/yourserver/nodevenv/yourdirectory/20/lib/node_modules/sharp/lib/index.js

Enter fullscreen mode Exit fullscreen mode

Yeyy! sharp was loading correctly. But, after removing the debug code from server.js, the original error came back:

⨯ Error: 'sharp' is required to be installed in standalone mode for image optimization to function correctly. Read more at: https://nextjs.org/docs/messages/sharp-missing-in-production
Enter fullscreen mode Exit fullscreen mode

The fix:

For some reason, Next.js couldn’t locate sharp. To resolve this, I set the NEXT_SHARP_PATH environment variable to explicitly point to the location of sharp:

NEXT_SHARP_PATH=/home/yourserver/nodevenv/yourdirectory/20/lib/node_modules/sharp/lib
Enter fullscreen mode Exit fullscreen mode

Image description

After saving and repeating the restart process, everything finally worked. The images were properly minified and transformed, just as expected.

Top comments (0)