DEV Community

Shafqat Awan
Shafqat Awan

Posted on

Building a Real CI/CD Orchestrator from Scratch (Node.js + GitHub + Railway)

Perfect topic for dev.to — this kind of “build CI/CD from scratch”

 Modern CI/CD tools like GitHub Actions, GitLab CI, and Jenkins often feel like magic boxes driven by YAML files. But what actually happens behind the scenes?

In this tutorial, we build a real, event-driven CI/CD platform from scratch, using Node.js, GitHub Webhooks, and Railway Cloud — no YAML, no plugins, just clean code and clear control.

By the end, you’ll understand how CI/CD systems really work internally, not just how to configure them.


🧠 What We’re Building

We design a custom CI/CD orchestrator that:

  • Listens to GitHub webhook events
  • Verifies webhook authenticity using HMAC signatures
  • Extracts branch and commit context
  • Triggers a deployment pipeline
  • Redeploys a real production payment service
  • Runs entirely on Railway Cloud

This is not a demo — it’s a real production-style pipeline.


🏗️ High-Level Architecture

Here’s the architecture we implement:

Developer Pushes Code
        ↓
GitHub Repository
        ↓
GitHub Webhook Event
        ↓
Webhook Server (Node.js)
        ↓
Signature Verification (HMAC)
        ↓
Deployment Orchestrator
        ↓
Pipeline Execution Logic
        ↓
Railway GraphQL API
        ↓
Payment Service Redeployed
Enter fullscreen mode Exit fullscreen mode

Key Design Principle

The orchestrator controls deployments — services never self-deploy.

This mirrors how real CI/CD platforms work internally.


🔧 Technology Stack

Core Technologies

  • Node.js (Express) – Webhook server & orchestrator
  • GitHub Webhooks – Event-driven triggers
  • Crypto (HMAC SHA-256) – Security & signature verification
  • Railway Cloud – Infrastructure & deployments
  • Railway GraphQL API – Programmatic redeployments

📦 System Components Breakdown

1️⃣ GitHub Repository

  • Hosts the payment service
  • Configured with a webhook pointing to our server
  • Sends events on every push

2️⃣ Webhook Server (Node.js)

The webhook server is responsible for:

  • Receiving GitHub events
  • Verifying request authenticity
  • Extracting branch & payload
  • Forwarding events to the orchestrator

Signature Verification (Critical Security Layer)

GitHub signs each webhook request using a shared secret.
We validate it using HMAC:

function verifySignature(req) {
  const sig = req.headers['x-hub-signature-256'];
  const hmac = crypto.createHmac('sha256', GITHUB_SECRET);
  const digest = "sha256=" + hmac.update(JSON.stringify(req.body)).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(digest));
}
Enter fullscreen mode Exit fullscreen mode

This ensures:

  • Requests really came from GitHub
  • Payloads were not modified
  • Unauthorized triggers are blocked

3️⃣ Branch-Aware Event Processing

const branch = req.body.ref?.split("/").pop() || "main";
Enter fullscreen mode Exit fullscreen mode

This allows:

  • Branch-based pipelines
  • Different deployment strategies per branch
  • Easy future extension (staging, prod, hotfix)

🧠 The Orchestrator (Heart of the System)

The orchestrator is a pipeline controller, similar to GitHub Actions or Jenkins — but implemented in code.

Responsibilities

  • Register pipelines
  • Map pipelines to branches
  • Execute pipeline steps sequentially
  • Handle failures gracefully

Orchestrator Design

class Orchestrator {
  constructor() {
    this.pipelines = {};
  }

  registerPipeline(branch, steps) {
    this.pipelines[branch] = steps;
  }

  async trigger(branch, payload) {
    const steps = this.pipelines[branch] || this.pipelines["main"];
    if (!steps) return;

    for (const step of steps) {
      await step(payload);
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Pipeline Registration

orchestrator.registerPipeline("main", [runPaymentPipeline]);
Enter fullscreen mode Exit fullscreen mode

This line means:

“Whenever main branch changes, run this deployment pipeline.”


🚀 The Deployment Pipeline

Instead of installing, testing, and building locally, we let Railway handle infrastructure, just like real cloud-native CI/CD systems.

Pipeline Responsibilities

  • Authenticate with Railway
  • Trigger redeployment
  • Monitor API response
  • Fail safely if deployment fails

Payment Service Pipeline

export async function runPaymentPipeline() {
  const response = await fetch(
    "https://backboard.railway.app/graphql/v2",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.RAILWAY_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        query: `
          mutation DeployService($serviceId: String!, $environmentId: String!) {
            serviceInstanceRedeploy(
              serviceId: $serviceId,
              environmentId: $environmentId
            )
          }
        `,
        variables: {
          serviceId: PAYMENT_SERVICE_ID,
          environmentId: ENVIRONMENT_ID,
        },
      }),
    }
  );
}
Enter fullscreen mode Exit fullscreen mode

This mirrors how:

  • GitHub Actions
  • GitLab CI
  • CircleCI

trigger deployments internally via provider APIs.


🔐 Why This Architecture Matters

Advantages Over Traditional CI/CD

✅ Full control over pipeline logic
✅ No YAML complexity
✅ Easier debugging
✅ Clear separation of concerns
✅ Extendable to any cloud provider


🔮 What You Can Build Next

This foundation can easily be extended to:

  • Multiple services
  • Multiple environments (dev, staging, prod)
  • Manual approvals
  • Rollbacks
  • Notifications (Slack, Email)
  • Full internal platform tooling

🎯 Who Should Read This?

This guide is perfect for:

  • Backend engineers curious about DevOps internals
  • Developers tired of copy-pasting CI configs
  • Platform engineers
  • Anyone who wants to truly understand CI/CD

🧩 Final Thoughts

CI/CD isn’t magic — it’s just event handling, orchestration, and APIs.

By building your own orchestrator, you don’t just deploy code —
you gain architectural clarity and engineering confidence.

If you enjoyed this deep dive, consider exploring:

  • Multi-stage pipelines
  • Canary deployments
  • Platform engineering concepts

Happy building 🚀
– CodingMavrick
For full video tutorial watch:- Build Your Own CI/CD Pipeline from Scratch | Node.js DevOps [https://youtu.be/7jlC-Svus9M]


Top comments (0)