<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Built Local CI/CD pipeline with Jenkins]]></title><description><![CDATA[Built Local CI/CD pipeline with Jenkins]]></description><link>https://ci-cd-jenkins-ngrok.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 12 Nov 2026 07:11:01 GMT</lastBuildDate><atom:link href="https://ci-cd-jenkins-ngrok.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Built a Local CI/CD Pipeline with Jenkins, GitHub Webhooks & ngrok (Beginner-Friendly)]]></title><description><![CDATA[Step-by-step guide to local CI/CD: run Jenkins (Docker), expose it with ngrok, configure GitHub webhooks, and auto-trigger Jenkinsfile builds.]]></description><link>https://ci-cd-jenkins-ngrok.hashnode.dev/how-i-built-a-local-cicd-pipeline-with-jenkins-github-webhooks-and-ngrok-beginner-friendly</link><guid isPermaLink="true">https://ci-cd-jenkins-ngrok.hashnode.dev/how-i-built-a-local-cicd-pipeline-with-jenkins-github-webhooks-and-ngrok-beginner-friendly</guid><category><![CDATA[Jenkins]]></category><category><![CDATA[ Jenkins, DevOps]]></category><category><![CDATA[Devops]]></category><category><![CDATA[Developer]]></category><category><![CDATA[Devops articles]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[groovy]]></category><category><![CDATA[ngrok]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Adheeb Anvar]]></dc:creator><pubDate>Sat, 30 Aug 2025 14:41:51 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756561183923/c3dd6cad-7ca0-49ab-87a8-4e6eba7ea1b2.png" alt="Jenkins" class="image--center mx-auto" /></p>
<h2 id="heading-why-this-is-worth-your-time-and-why-most-tutorials-suck">Why this is worth your time (and why most tutorials suck)</h2>
<p>Most CI/CD guides assume you run Jenkins on a cloud VM or use GitHub Actions. That's fine , but if you're learning, you want full control and zero cloud bills. Running Jenkins locally teaches the basics first. Once you understand that, moving to the cloud will become very easy and efficient.</p>
<hr />
<h2 id="heading-what-youll-build">What you'll build</h2>
<ul>
<li><p>Jenkins running locally (Docker recommended)</p>
</li>
<li><p>Ngrok exposing Jenkins to the internet</p>
</li>
<li><p>A GitHub webhook pointing to ngrok</p>
</li>
<li><p>A Jenkinsfile in your repo that runs automatically on push</p>
</li>
</ul>
<hr />
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Docker (for Jenkins) or Jenkins installed locally</p>
</li>
<li><p>GitHub repo you control</p>
</li>
<li><p>ngrok account (free tier works)</p>
</li>
<li><p>Basic terminal + Git knowledge</p>
</li>
</ul>
<hr />
<h2 id="heading-quick-architecture-one-liner">Quick architecture (one-liner)</h2>
<p><code>GitHub (push) → webhook → ngrok → local Jenkins → runs Jenkinsfile (Build → Test → Deploy)</code></p>
<hr />
<h2 id="heading-step-0-quick-setup-checklist-commands-youll-need">Step 0 — Quick setup checklist (commands you'll need)</h2>
<pre><code class="lang-bash"><span class="hljs-comment"># start Jenkins in Docker (recommended)</span>
docker run -p 8080:8080 -p 50000:50000 -d \
  -v jenkins_home:/var/jenkins_home \
  jenkins/jenkins:lts-jdk17

<span class="hljs-comment"># start ngrok to expose Jenkins</span>
ngrok http 8080
</code></pre>
<h2 id="heading-step-1-start-jenkins-locally">Step 1 — Start Jenkins locally</h2>
<ol>
<li><p>Run the docker run command above. (Yes, you need the volume so your Jenkins data survives restarts.)</p>
</li>
<li><p>Open <a target="_blank" href="http://localhost:8080"><code>http://localhost:8080</code></a> and finish initial setup (install suggested plugins, create admin user).</p>
</li>
<li><p>Make sure <strong>Git</strong>, <strong>GitHub</strong>, and <strong>Pipeline</strong> related plugins are installed:</p>
<ul>
<li><p>Git Plugin</p>
</li>
<li><p>GitHub Plugin</p>
</li>
<li><p>GitHub Branch Source (if using multibranch)</p>
</li>
<li><p>Pipeline: Multibranch (or Pipeline if single-branch)</p>
</li>
</ul>
</li>
</ol>
<p><strong>Common gotcha:</strong> Jenkins ignores ‘jenkinsfile’ lowercase — the file must be called Jenkinsfile (capital J).</p>
<hr />
<h2 id="heading-step-2-expose-jenkins-with-ngrok">Step 2 — Expose Jenkins with ngrok</h2>
<p>GitHub cannot reach localhost. ngrok creates a public URL that tunnels to your machine.</p>
<ol>
<li><p>Download &amp; sign up: go to <a target="_blank" href="http://ngrok.com">ngrok.com</a> and get your auth token.</p>
</li>
<li><p>Authenticate and run:</p>
<pre><code class="lang-bash"> ngrok config add-authtoken &lt;YOUR_TOKEN&gt;
 ngrok http 8080
</code></pre>
</li>
<li><p>Note the <a target="_blank" href="https://xxxxxx.ngrok-free.app"><code>https://xxxxxx.ngrok-free.app</code></a> forwarding URL. That's your public Jenkins address.</p>
</li>
</ol>
<p><strong>Important:</strong> ngrok free URLs are temporary, they change each run. Expect to update the webhook when the URL changes.</p>
<hr />
<h2 id="heading-step-3-add-github-webhook">Step 3 — Add GitHub webhook</h2>
<ol>
<li><p>In your GitHub repo → <strong>Settings → Webhooks → Add webhook</strong>.</p>
</li>
<li><p><strong>Payload URL</strong>: <code>https://&lt;your-ngrok-id&gt;.</code><a target="_blank" href="http://ngrok-free.app/github-webhook/"><code>ngrok-free.app/github-webhook/</code></a></p>
</li>
<li><p><strong>Content type</strong>: application/json</p>
</li>
<li><p><strong>Events</strong>: select <strong>Just the push event</strong></p>
</li>
<li><p>Save.</p>
</li>
</ol>
<p><strong>Test:</strong> GitHub will send a <code>ping</code> event immediately. Check the webhook's <strong>Recent Deliveries</strong> — you should see a 200 response if Jenkins/ngrok are reachable.</p>
<p><strong>Common fail:</strong> If GitHub shows Content type:form or delivery status "failed", fix content type to application/json and ensure ngrok is running.</p>
<hr />
<h2 id="heading-step-4-create-your-jenkins-job-multibranch-or-pipeline">Step 4 — Create your Jenkins job (multibranch or pipeline)</h2>
<p>You have two realistic choices:</p>
<h3 id="heading-a-multibranch-pipeline-recommended-if-you-want-branch-per-pipeline">A. Multibranch Pipeline (recommended if you want branch-per-pipeline)</h3>
<ul>
<li><p>Jenkins dashboard → <strong>New Item</strong> → <strong>Multibranch Pipeline</strong> → name it.</p>
</li>
<li><p><strong>Branch Sources</strong> → Add → <strong>Git</strong> or <strong>GitHub</strong> → paste repo URL.</p>
</li>
<li><p>Add credentials if your repo is private (Personal Access Token recommended).</p>
</li>
<li><p>Save.</p>
</li>
</ul>
<p>Jenkins will scan branches and create per-branch pipelines automatically when it finds a <strong>Jenkinsfile</strong> in branch roots.</p>
<h3 id="heading-b-single-pipeline-if-you-only-care-about-main">B. Single Pipeline (if you only care about main)</h3>
<ul>
<li>New Item → Pipeline → Pipeline definition from SCM → Git → repo URL, credentials, script path Jenkinsfile.</li>
</ul>
<p><strong>Note:</strong> For multibranch, you don't tick "Pipeline script from SCM" — Jenkins manages the Jenkinsfile discovery itself.</p>
<hr />
<h2 id="heading-step-5-add-a-jenkinsfile-to-your-repo">Step 5 — Add a Jenkinsfile to your repo</h2>
<p>Place Jenkinsfile at the <strong>root</strong> of the branch (e.g., main). Example (Declarative — recommended):</p>
<pre><code class="lang-markdown">pipeline {
  agent any

  stages {
<span class="hljs-code">    stage('Greet') {
      steps { echo 'Hello world' }
    }
</span>
<span class="hljs-code">    stage('Build') {
      steps { sh 'echo Building...' }
    }
</span>
<span class="hljs-code">    stage('Test') {
      steps { sh 'echo Running tests...' }
    }
</span>
<span class="hljs-code">    stage('Deploy') {
      steps { sh 'echo Deploying...' }
    }
  }
</span>
  post {
<span class="hljs-code">    success { echo 'Pipeline Success' }
    failure { echo 'Pipeline Failed' }
  }
}</span>
</code></pre>
<hr />
<h2 id="heading-step-6-wire-jenkins-to-github-root-url-amp-plugins">Step 6 — Wire Jenkins to GitHub (root URL &amp; plugins)</h2>
<ol>
<li><p>Jenkins → <strong>Manage Jenkins → Configure System</strong> → set <strong>Jenkins URL</strong> to your ngrok URL (e.g., <a target="_blank" href="https://abcd.ngrok-free.app/"><code>https://abcd.ngrok-free.app/</code></a>). This lets Jenkins build correct webhook links.</p>
</li>
<li><p>Ensure <strong>GitHub plugin</strong> and <strong>GitHub Branch Source</strong> are installed.</p>
</li>
<li><p>For Multibranch: under the job's config, add the GitHub repo as Branch Source and set credentials.</p>
</li>
<li><p>Enable <strong>Scan Multibranch Pipeline Triggers</strong> (use webhook as primary; schedule a short periodic scan like <code>@hourly</code> or 1 min as fallback).</p>
</li>
</ol>
<hr />
<h2 id="heading-step-7-test-push-code-and-watch-it-run">Step 7 — Test: push code and watch it run</h2>
<ol>
<li><p>Commit a trivial change to README.md (or update Jenkinsfile) and git push.</p>
</li>
<li><p>Watch ngrok terminal — you'll see a <code>POST /github-webhook/</code> hit.</p>
</li>
<li><p>Jenkins should now see the webhook and run a build. In Jenkins:</p>
<ul>
<li><p>Open job → click the branch → click latest build (#1) → <strong>Console Output</strong>.</p>
</li>
<li><p>You'll see the echoi and sh outputs for each stage.</p>
</li>
</ul>
</li>
</ol>
<hr />
<h2 id="heading-what-i-learned-short-amp-honest">What I learned (short &amp; honest)</h2>
<ul>
<li><p>Local-first CI/CD is perfect for learning: you see every step.</p>
</li>
<li><p>ngrok is the fastest way to expose local services for webhooks.</p>
</li>
<li><p>Jenkins is powerful but picky: file names, pipeline syntax, and plugins all matter.</p>
</li>
<li><p>Multibranch pipelines remove manual job creation; Jenkinsfile is the very easy to create.</p>
</li>
</ul>
<hr />
<h2 id="heading-next-practical-steps-what-id-do-next">Next practical steps (what I'd do next)</h2>
<ul>
<li><p>Add real tests (Pytest, Jest, JUnit) into the Test stage and fail the build if tests fail.</p>
</li>
<li><p>Use Docker agent in pipeline: run stages inside containers for clean builds.</p>
</li>
<li><p>Push Docker images to Docker Hub from Jenkins.</p>
</li>
<li><p>Migrate Jenkins from local/ngrok to a small VPS or cloud VM when you want a persistent endpoint.</p>
</li>
<li><p>Add notifications (Slack / Email) and artifact archiving.</p>
</li>
</ul>
<hr />
<h2 id="heading-faq-jenkins-ngrok-github-webhook-quick-practical-answers">FAQ — Jenkins + ngrok + GitHub Webhook (quick, practical answers)</h2>
<h3 id="heading-1-what-is-a-jenkinsfile-and-why-do-i-need-it">1: What is a <strong>Jenkinsfile</strong> and why do I need it?</h3>
<p>A: Jenkinsfile is the pipeline-as-code file that tells Jenkins what to run (stages, steps, environment, post actions). Put it in your repo so Jenkins can version-control the build logic with your code.</p>
<p>2: Does Jenkinsfile have to live in the repo root?</p>
<p>A: By default, yes. Jenkins looks for Jenkinsfile at the branch root. You <em>can</em> keep it in a subfolder (e.g., ci/Jenkinsfile) but must update the <strong>Script Path</strong> (or multibranch source) to point to that path.</p>
<h3 id="heading-3-what-is-the-difference-between-multibranch-pipeline-and-pipeline-job">3: What is the difference between Multibranch Pipeline and Pipeline job?</h3>
<p><strong>A: Multibranch Pipeline</strong>: Jenkins auto-scans branches and runs a pipeline for each branch that contains a Jenkinsfile. Whereas <strong>Single Pipeline job</strong>: Tied to one branch/script path. Simpler for single-branch projects.</p>
<h3 id="heading-4-is-ngrok-safe-to-use-any-alternatives">4: Is ngrok safe to use? Any alternatives?</h3>
<p>A: Ngrok is fine for local dev/testing. It exposes a public endpoint to your machine, so treat it like a public service. Alternatives: <strong>Cloudflare Tunnel</strong>, <strong>LocalTunnel</strong>, <strong>FRP</strong> (self-hosted). For production use a proper cloud Jenkins instance or VPN-protected tunnel.</p>
<h3 id="heading-5-my-webhook-shows-ping-ok-but-no-build-runs-why">5: My webhook shows ping OK but no build runs — why?</h3>
<p>A:ping only verifies delivery. After that: ensure webhook event is <strong>push</strong>, content-type application/json, and Jenkins is configured for that repo (branch source with credentials). Also inspect Jenkins <em>Scan Multibranch Pipeline Log</em> for errors.</p>
<p>6: Where’s The code?</p>
<p>A:<a target="_blank" href="https://github.com/adheeb2/learn-ci-cd-with-jenkins-docker">https://github.com/adheeb2/learn-ci-cd-with-jenkins-docker</a></p>
<hr />
<p><em>Built by Adheeb Anvar — Conversational AI Developer</em></p>
]]></content:encoded></item></channel></rss>