Skip to content

πŸš€ How to Create Your Own GitHub App (Step-by-Step Guide) ​

Author: Mark Wayne B. Menorca
Organization: Cloudmateria / Wayne Enterprise Solution Corporation
Last Updated: October 2025


🧠 Overview ​

A GitHub App lets you integrate automation, CI/CD, bots, or custom workflows directly into GitHub β€” with granular permissions and event-based webhooks.

You can use a GitHub App to:

  • Automate code scanning or validation
  • Manage issues, pull requests, and commits
  • Add custom status checks or CI gates
  • Integrate with your internal systems (like Slack, Notion, or servers)
  • Implement custom rules (e.g., prevent committing .env files)

In this guide, we’ll build a real GitHub App from scratch β€” deploy it, and make it perform automated tasks (like scanning for .env files) using Node.js + Express + Octokit.


🧩 Prerequisites ​

Before you start, make sure you have:

  • A GitHub account or organization
  • Node.js β‰₯ 18 installed
  • Basic knowledge of JavaScript and npm
  • A server or cloud platform (Render, Railway, Vercel, or VPS) with HTTPS

🧱 Step 1: Create a New GitHub App ​

  1. Go to your GitHub Developer Settings page:
    πŸ‘‰ https://github.com/settings/apps/new

  2. Fill in the required information:

FieldDescriptionExample
GitHub App nameA unique name for your appCloudmateria EnvGuard
Homepage URLYour app’s homepagehttps://yourapp.onrender.com
Callback URLRequired, even if unused (OAuth placeholder)https://yourapp.onrender.com/auth
Webhook URLThe URL where GitHub will send webhook eventshttps://yourapp.onrender.com/github/webhook
Webhook SecretRandom secure string for verifying authenticitygh_secret_12345

πŸ“œ Permissions ​

Under Repository Permissions, choose what your app needs access to:

PermissionAccess LevelPurpose
ChecksRead & WriteTo post Check Runs (status checks)
IssuesRead & WriteTo create or comment on issues
ContentsRead-onlyTo analyze commits and file changes
Pull requestsRead-onlyTo respond to PRs
MetadataRead-onlyAlways required

πŸ”” Subscribe to Events ​

Scroll to Subscribe to Events, and check:

  • βœ… push
  • βœ… pull_request

Click Create GitHub App.


πŸ”‘ Step 2: Generate Your App Credentials ​

After creating the app:

  1. Download Private Key (.pem)
    This is your app’s authentication key (used to sign JWTs).
    Save it securely β€” never commit it to GitHub.

  2. Note down these values:

    • App ID
    • Client ID
    • Webhook Secret
    • Your downloaded github-app.pem

☁️ Step 3: Deploy a Simple Node.js Server ​

Let’s build the backend to receive webhooks and process them.

1️⃣ Initialize project ​

bash
mkdir my-github-app
cd my-github-app
npm init -y
npm install express @octokit/app @octokit/webhooks dotenv

2️⃣ Create .env ​

bash
GITHUB_APP_ID=123456
GITHUB_WEBHOOK_SECRET=your_secret
PORT=3000

3️⃣ Create server.js ​

js
import express from "express";
import { App } from "@octokit/app";
import { Webhooks } from "@octokit/webhooks";
import dotenv from "dotenv";

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

// πŸ—οΈ Private key (load from environment)
const privateKey = process.env.GITHUB_PRIVATE_KEY?.replace(/\\n/g, "\n");

const githubApp = new App({
  appId: process.env.GITHUB_APP_ID,
  privateKey,
});

const webhooks = new Webhooks({
  secret: process.env.GITHUB_WEBHOOK_SECRET,
});

// βœ… Webhook route (modern Node 18+ compatible)
app.post("/github/webhook", async (req, res) => {
  try {
    await webhooks.verifyAndReceive({
      id: req.headers["x-github-delivery"],
      name: req.headers["x-github-event"],
      signature: req.headers["x-hub-signature-256"],
      payload: req.body,
    });
    res.status(200).send("Webhook received βœ…");
  } catch (error) {
    console.error("❌ Webhook verification failed:", error.message);
    res.status(400).send("Invalid signature");
  }
});

// 🧩 Example event listener
webhooks.on(["push", "pull_request"], async ({ payload }) => {
  console.log("πŸ“¦ Received event:", payload.action || "push");
});

app.listen(process.env.PORT || 3000, () => {
  console.log(`πŸš€ GitHub App listening on port ${process.env.PORT || 3000}`);
});

🧭 Step 4: Configure Your App URLs ​

In your GitHub App settings, go to General β†’ Webhook:

FieldValue
Webhook URLhttps://yourapp.onrender.com/github/webhook
Webhook Secretsame as .env
Callback URLhttps://yourapp.onrender.com/auth (placeholder)

☁️ Step 5: Deploy to Render (or another host) ​

Render Example: ​

  1. Go to Render.com
  2. Click New β†’ Web Service
  3. Connect your GitHub repository
  4. Build Command β†’ npm install
    Start Command β†’ node server.js
  5. Add Environment Variables:
    • GITHUB_APP_ID
    • GITHUB_WEBHOOK_SECRET
    • GITHUB_PRIVATE_KEY (paste full PEM content)

Render automatically gives you a live HTTPS URL like:

https://yourapp.onrender.com

πŸ§ͺ Step 6: Test It ​

  1. Go to your GitHub App β†’ Install App
  2. Install it on a test repository
  3. Push a commit or open a PR
  4. Check your Render logs β€” you should see:
    πŸ“¦ Received event: push
    Webhook received βœ…
    

🧱 Step 7: Add Functionality ​

Now that your app receives webhooks, you can extend it!

Example: Create an Issue Automatically ​

js
webhooks.on("push", async ({ payload }) => {
  const { repository, installation } = payload;
  const owner = repository.owner.login;
  const repo = repository.name;

  const octokit = await githubApp.getInstallationOctokit(installation.id);
  await octokit.request("POST /repos/{owner}/{repo}/issues", {
    owner,
    repo,
    title: "🚨 New Push Event Detected",
    body: "This issue was created automatically by your GitHub App.",
  });

  console.log(`🧾 Issue created in ${owner}/${repo}`);
});

πŸ”’ Step 8: Add a Custom Status Check ​

js
await octokit.request("POST /repos/{owner}/{repo}/check-runs", {
  owner,
  repo,
  name: "Custom Validation Check",
  head_sha: payload.after,
  status: "completed",
  conclusion: "success",
  output: {
    title: "βœ… Custom Check Passed",
    summary: "Everything looks good!",
  },
});

This will appear under the Checks tab in the pull request.


🧾 Step 9: Enforce Merge Rules ​

In your repository:

  1. Go to Settings β†’ Branches β†’ Branch Protection Rules
  2. Enable:
    • βœ… Require status checks to pass before merging
    • βœ… Select your app’s check (e.g. β€œCustom Validation Check”)
  3. Save β†’ GitHub now blocks merges until your app’s check passes.

🧹 Step 10: Secure and Maintain ​

TaskFrequencyDescription
πŸ”‘ Rotate PEM keyEvery 6–12 monthsRegenerate from GitHub App settings
🧰 Monitor logsWeeklyCheck webhook delivery reports
🚫 Never commit PEMAlwaysAdd to .gitignore
πŸ” Use environment secretsAlwaysStore credentials in Render/Host env vars
🧩 Test webhook deliveryOn new deployUse GitHub β€œRecent Deliveries” panel

🧠 Example .gitignore ​

bash
# Node and credentials
node_modules/
.env
*.pem

🧾 Common Issues ​

ErrorCauseSolution
Invalid signatureWebhook secret mismatchEnsure GitHub App secret matches .env
argument handler must be a functionOld middleware versionUse verifyAndReceive() instead
Required status check missingWrong check name in protection rulesUpdate rule to match your check name
Webhook not receivedWrong webhook URLUse full HTTPS Render URL + /github/webhook

βœ… Summary ​

StepDescription
1️⃣Create a new GitHub App
2️⃣Set permissions & webhook events
3️⃣Download private key (.pem)
4️⃣Build Node.js backend with Octokit
5️⃣Deploy on Render or Railway
6️⃣Install the App on repositories
7️⃣Add automation: issues, checks, alerts
8️⃣Add branch protection for enforced security

πŸ“Ž References ​


🏁 Conclusion ​

Creating your own GitHub App allows you to automate your workflows securely and at scale.
Whether it’s running checks, creating issues, or scanning commits, a self-hosted GitHub App gives you complete control over your development pipeline β€” with zero dependency on external services.

Once you’ve mastered this foundation, you can extend your app with:

  • AI-driven code reviews
  • Automated pull request templates
  • Secret scanning (like EnvGuard)
  • Slack or Discord notifications

Build once β€” automate forever. βš™οΈπŸ’‘