Building a GitHub Action Notifier with n8n (Self-Hosted)

1/9/2026

In this post, I’ll walk through how I built a GitHub Action notification system using self-hosted n8n, and how I send meaningful alerts to our Discord instead of the default notifications.

This setup is ideal if you:

  • Self-host your automation tools
  • Want full control (no SaaS limits)
  • Care about clean, actionable alerts
  • Are already using GitHub actions

This blog focuses only on GitHub → n8n → Discord.


🛑 Why not just use GitHub notifications?

GitHub notifications are fine… until they aren’t.

What I ran into:

  • Too many emails
  • No context
  • Hard to filter what’s critical
  • No easy way to format messages nicely
  • Team not noticing

What I wanted instead:

  • Immediate alerts for failures & successes
  • Interactive messages
  • Different severity levels
  • One central place (Discord)

That’s where n8n fits perfectly.


🏗️ Architecture Overview

Here’s the final flow:

n8n GitHub flow

✅ No GitHub apps
✅ No polling
✅ No paid services


⚙️ Prerequisites

Before starting, make sure you have:

  • A n8n instance
  • A public HTTPS (Recommended) URL for n8n
  • A Discord webhook
  • A GitHub repository with Actions enabled

⚠️ GitHub requires HTTPS.


🌐 Step 1: Expose n8n via HTTPS

I’m running n8n behind Nginx with Docker in my own VPS.

Things to double-check:

  • SSL certificate (Let’s Encrypt)
  • WebSocket support
  • Reverse proxy headers
  • Correct n8n environment variables

🧠 Must-have Nginx config for WebSockets

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Without this, the n8n UI will randomly show “connection lost”.

🐳 Step 2: n8n Docker Configuration

Here’s the docker-compose.yml I used:

version: "3.3"

services:
  n8n:
    image: n8nio/n8n
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - N8N_PROTOCOL=https
      - N8N_HOST=n8n.yourdomain.com
      - N8N_EDITOR_BASE_URL=https://n8n.yourdomain.com
      - WEBHOOK_URL=https://n8n.yourdomain.com
      - N8N_TRUST_PROXY=true
      - N8N_PROXY_HOPS=1
      - GENERIC_TIMEZONE=Asia/Tokyo
    volumes:
      - ./n8n_data:/home/node/.n8n

This sets correct base URLs for both UI and webhooks.

📩 Step 3: Create a Webhook in n8n

Inside n8n:

  1. Add Webhook node
  2. Method: POST
  3. Path: github-action
  4. Save the workflow

Your webhook URL becomes:

https://n8n.yourdomain.com/webhook/github-action

🛠️ Step 4: Connect GitHub to n8n using Webhooks

In the GitHub Repository, configure a Webhook as below.

1️⃣ Go to your GitHub repository

Repository → Settings → Webhooks → Add webhook

2️⃣ Webhook configuration

FieldValue
Payload URLhttps://n8n.yourdomain.com/webhook/github-action
Content typeapplication/json
Secret(optional)
SSL verificationEnabled
EventsWorkflow runs

3️⃣ Save the webhook

GitHub will now POST workflow data to n8n every time a workflow completes.

🧠 What GitHub Sends to n8n

GitHub automatically sends a payload like:

  • Repository name
  • Workflow name
  • Branch
  • Conclusion (success, failure)
  • Run URL
  • Commit details

No extra work needed.

🔍 Step 5: Inspect & Normalize the GitHub Payload in n8n

Once GitHub sends data to your webhook, execute the workflow once and inspect the payload.

In the Webhook node output, you’ll see a structure similar to:

{
  "workflow_run": {
    "name": "CI",
    "conclusion": "failure",
    "html_url": "https://github.com/org/repo/actions/runs/123",
    "head_branch": "main"
  },
  "repository": {
    "full_name": "org/repo"
  }
}

Key fields we’ll use:

  • workflow_run.name
  • workflow_run.conclusion
  • workflow_run.html_url
  • workflow_run.head_branch
  • repository.full_name

This data is enough to build meaningful alerts.

🧠 Step 6: Add Logic (Success vs Failure)

Add an IF node after the Webhook.

IF condition

Check workflow result:

Left value:

{{ $json.workflow_run }}

Check whether the workflow_run exists

workflow_run exists

Result paths

TRUE → Next IF Condition

This gives you an error less routing.

🧠 Step 7: Check whether the workflow was completed

Sometimes the alerts from incomplete (cases like manually stopped) actions might also arrive to the n8n workflow.

So I added a condition to check whether the workflow was completed

{{$json.body.workflow_run.status}}

workflow_run exists

💬 Step 8: Send Alerts to Discord

Instead of using multiple Discord nodes, this workflow uses one Discord node with conditional message formatting.

This keeps the flow:

  • Cleaner
  • Easier to maintain
  • Less visual clutter in n8n

The severity (success vs failure) is handled inside the message itself.

🧩 How It Works

Add one Discord node after your logic/formatting step

Use n8n expressions to dynamically change the message

Emojis + text indicate severity

No branching required.

📝 Discord Message Template

Here’s the exact message format used:

Repository: {{ $json.body.repository.full_name }}
Workflow: {{ $json.body.workflow_run.name }}
Branch: {{ $json.body.workflow_run.head_branch }}

{{
  $json.body.workflow_run.conclusion === 'success'
    ? '✅ Successfully deployed to the ' + $json.body.workflow_run.head_branch + ' environment'
    : '❌ Run Failed'
}}

🔗{{ $json.body.workflow_run.html_url }}

🧩 How the message logic works What This Does

  • Gets all the details from the github action payload

  • Assign the values neccessary for the messsage

  • Check whether the conslusion of the workflow run is success,

  • If success then the success message will be set, else the failed part will be set to the message

🏁 Closing Thoughts

With this setup in place, Now the team gets a clear, real-time GitHub Action notification directly in Discord, without relying on GitHub’s default emails or individual messages.

This workflow has already reduced waiting time on informing changes in the code.


🙌 Let’s Connect

If you’re into futuristic tech, building cool apps, or just curious about life in Japan as a software engineer — follow along!

📸 Instagram
🐦 X / Twitter
🌐 dhaneja.com

Let’s see what the future holds 👓🚀