Deploy Next JS App To Cpanel Using Github Actions
Published: 2023-11-04 17:01:35

Deployment can be a challenging and time-consuming process if you zip files manually, put the zip on the server, and extract the files each time🥴

Github actions solve this problem by giving us an automated CI/CD platform.

## **Prerequisite**

- FTP account created in cpanel ([Check this link](https://support.cpanel.net/hc/en-us/articles/1500012573002-How-to-create-an-FTP-account))
- Configuration of FTP Details in github repository secrets, if you don't know how to add one, you can check this [link](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) out

You should have 3 mandatory repository secrets in your repo settings

- FTP_HOST
- FTP_USERNAME
- FTP_PASSWORD

## **Setting Up Cpanel**

Before proceeding, create an empty nodejs app in CPanel with the application startup file named as `server.js`, you can visit this [link](https://www.inmotionhosting.com/support/edu/cpanel/setup-node-js-app/) for reference

## **Process**

In your `next.config.js`, add the following line

```
  distDir: "_next",
```

Create a file named as `server.js` in root of your directory and add the following code in it

```
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 || 3000;

const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  createServer(async (req, res) => {
    try {
      const parsedUrl = parse(req.url, true);
      const { pathname, query } = parsedUrl;

      if (pathname === "/a") {
        await app.render(req, res, "/a", query);
      } else if (pathname === "/b") {
        await app.render(req, res, "/b", query);
      } else {
        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}`);
  });
});
```

Create a file named `deploy.yml`, the path of the file should be

`/projectpath/.github/workflows/deploy.yml`

Look I am not going to take much of your time, so if you only want to know the code, here it is.

```
name: Deploy to FTP

on:
  push:
    branches:
      - master  # Adjust the branch name if needed

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: 14.21.2  # Use the desired Node.js version

      - name: Install dependencies and build Next.js app
        run: |
          yarn install  # Install project dependencies
          touch .env.local
          echo MONGO_URL=${{ secrets.MONGO_URL }} >> .env.local
          echo JWT_SECRET=${{ secrets.JWT_SECRET }} >> .env.local
          yarn build  # Build Next.js app

      - name: Deploy via FTPS
        uses: SamKirkland/FTP-Deploy-Action@4.3.3
        with:
          server: ${{ secrets.FTP_HOST }}
          username: ${{ secrets.FTP_USERNAME }}
          password: ${{ secrets.FTP_PASSWORD }}
          local-dir: ./  # This points to the build output directory of your Next.js app
          server-dir: /
          protocol: ftps
          timeout: 300000  # Set a longer timeout value in milliseconds (here, 2 minutes)
```
Lets breakdown steps

## Checkout Code

This action checks-out your repository under $GITHUB_WORKSPACE, so your workflow can access it.

## Set up Node.js

This step involves setting up your GitHub Actions workflow with a specific version of node.js, in our case, we set it up to 14.21.2, that's the lastest version upto which cpanel supports.

## Install dependencies and build Next.js app

In this step, we can run commands like yarn install/npm install to install the dependencies required to run our app. It also includes getting env values from repo secrets and setting them in `.env.local`

## Deploy via FTPS

This step involves deploying built code on the server using ftp account