Hanwool Codes RSS Tag Admin Write Guestbook
Web Development (1)
2025-04-16 04:25:02

If you’re running a Next.js application on an Ubuntu server, you probably want a clean workflow to update your production app every time you push to GitHub. In this guide, we’ll walk through setting up GitHub webhooks, a small server to receive them, and a deployment script that builds and restarts your app with PM2.


✅ Prerequisites

  • A public or private Ubuntu server
  • Node.js, npm, and PM2 installed
  • A running Next.js app managed by PM2
  • Your app hosted on GitHub (private or public)
  • SSH access to your server

Step 1: Generate SSH Key for GitHub (if needed)

On your Ubuntu server, generate an SSH key so the server can authenticate with GitHub and pull code automatically:

ssh-keygen -t rsa -b 4096 -C "your-github-email@example.com"

Press Enter to accept the default path (~/.ssh/id_rsa).
Now, copy the public key:

cat ~/.ssh/id_rsa.pub

Go to GitHub > Settings > SSH and GPG keys > New SSH key, and paste it in.
Test the connection:

ssh -T git@github.com

You should see a “You’ve successfully authenticated” message.


Step 2: Clone Your Repository

Pick a directory for your app and clone your GitHub repo:

cd /var/www
git clone git@github.com:your-username/your-repo.git
cd your-repo

Install dependencies and build the project:

npm install
npm run build

Start the app with PM2:

pm2 start npm --name "your-app-name" -- start
pm2 save

Step 3: Create the Deployment Script

Create a shell script that will handle updates:

nano ~/deploy.sh

Add this content:

#!/bin/bash

cd /var/www/your-repo

echo "Pulling latest changes..."
git pull origin main

echo "Installing dependencies..."
npm install

echo "Building the app..."
npm run build

echo "Restarting app with PM2..."
pm2 restart your-app-name

echo "Deployment complete!"

Make it executable:

chmod +x ~/deploy.sh

Step 4: Set Up a Webhook Listener

Create a simple Express server that listens for GitHub webhooks:

mkdir ~/webhook && cd ~/webhook
nano server.js

Paste the following code:

const express = require("express");
const { exec } = require("child_process");

const app = express();
app.use(express.json());

app.post("/webhook", (req, res) => {
  const payload = req.body;

  if (payload.ref === "refs/heads/main") {
    console.log("Push to main detected. Starting deployment...");
    exec("bash ~/deploy.sh", (err, stdout, stderr) => {
      if (err) {
        console.error(`Deployment error: ${err.message}`);
        return res.status(500).send("Deployment failed.");
      }
      console.log(stdout);
      console.error(stderr);
      res.status(200).send("Deployment complete.");
    });
  } else {
    res.status(200).send("Push ignored (not main branch).");
  }
});

const PORT = 4000;
app.listen(PORT, () => {
  console.log(`Webhook server listening on port ${PORT}`);
});

Initialize and install dependencies:

npm init -y
npm install express

Start the server using PM2:

pm2 start server.js --name webhook
pm2 save

Step 5: Open the Webhook Port (Optional)

If your firewall is active, allow the webhook port:

sudo ufw allow 4000

Step 6: Set Up GitHub Webhook

In your GitHub repo:

  1. Go to Settings > Webhooks > Add webhook
  2. Payload URL:
    http://your-server-ip:4000/webhook
  3. Content type:
    application/json
  4. Leave secret empty (or handle it in code if using)
  5. Events: Just the push event
  6. Save webhook

Step 7: Test the Deployment Flow

Make a small commit to the main branch:

git add .
git commit -m "Test auto-deploy"
git push origin main

Then monitor the webhook log:

pm2 logs webhook

You should see output indicating that the deployment script was triggered, the app was rebuilt, and restarted via PM2.


What Are the Advantages of Using This Webhook Setup?

  1. Hands-Free Deployment
    Every time you push to the main branch, your app automatically updates — no need to SSH into the server, pull manually, or restart services.

  2. Fast Feedback Loop
    Code changes go live in seconds, keeping development and production in sync. This is especially useful for small teams or solo developers.

  3. No External CI/CD Needed
    You don’t need to rely on tools like GitHub Actions, Jenkins, or CircleCI. Everything runs locally on your own server, which is great for simplicity and control.

  4. Customizable Deployment Logic
    You have full control over the deployment process. Want to run tests, update environment variables, or notify Slack? Just add it to your deploy.sh.

  5. Lightweight & Resource-Friendly
    Unlike full CI/CD tools that require containers or background jobs, this setup runs with minimal overhead — just a simple Express server and PM2.

  6. Works with Private Repos
    Since you're using SSH keys to authenticate with GitHub, this works equally well with private repositories.

  7. Secure by Design
    You can lock down access to the webhook port, use GitHub IP whitelisting, and add secret token verification for even more security.

  8. Great for VPS and Self-Hosted Environments
    Whether you're using DigitalOcean, Hetzner, Linode, or your own machine, this approach is ideal for traditional Ubuntu-based deployments.

'Web Development' 카테고리의 다른 글

Fixing JS and CSS Not Loading in Next.js on GitHub Pages  (0) 2025.04.10


Hanwool Codes. Designed by 코딩재개발.