DocumentationJenkins Plugin

Jenkins Plugin

Receive GitHub, GitLab and Bitbucket webhooks in Jenkins without a public IP using the native Webhook Relay plugin.

The Webhook Relay Jenkins plugin lets Jenkins receive webhooks from GitHub, GitLab and Bitbucket without exposing Jenkins to the public internet — no public IP, no reverse proxy, no inbound firewall rules. The plugin opens an outbound connection to a Webhook Relay bucket and forwards each webhook it receives to the matching Jenkins endpoint (/github-webhook/, /bitbucket-hook/, …), so your SCM can trigger builds even when Jenkins runs on a private network, behind a NAT, or on your laptop.

Unlike running the relay CLI as a separate agent, the plugin runs inside Jenkins — there is nothing else to install or keep alive. And because delivery goes through a bucket (the forwarding feature, not a tunnel), every webhook is recorded on the bucket's logs page together with the response Jenkins returned, which makes debugging deliveries trivial.

GitHub to Jenkins without public IP

How it works

GitHub / GitLab / Bitbucket
        │  (webhook)
        ▼
https://<your-bucket>.hooks.webhookrelay.com      ← public URL you paste into the SCM
        │
        ▼
   Webhook Relay bucket  ──────────────►  logs page (every request + Jenkins response)
        │  (outbound WebSocket opened by the plugin)
        ▼
   Jenkins  →  /github-webhook/  →  build triggered
  1. The plugin authenticates with your API token and subscribes to a bucket over a persistent outbound WebSocket. No inbound ports are opened on Jenkins.
  2. A webhook hits the bucket's public input URL and Webhook Relay streams it down the socket.
  3. The plugin replays the request against the Jenkins endpoint for your SCM and reports the response back to the bucket log.

Why not just expose Jenkins?

Because almost every Jenkins vulnerability that has been exploited in the wild came in through a part of the controller that a webhook never needs. GitHub only has to reach /github-webhook/. Attackers went for everything else:

CVEEndpoint attackers usedAuth needed
CVE-2024-23897: CLI file read, used by ransomware/cliNone for partial reads
CVE-2017-1000353: remoting CLI deserialization RCE/cli and the agent portNone
CVE-2018-1000861: Stapler URL routing, the base of a pre-auth RCE chainAny routed URLNone
CVE-2019-1003029 and CVE-2019-1003030: Groovy sandbox bypassesScript-check URLs, Pipeline configOverall/Read
CVE-2026-117612 et al.: 15 core bugs fixed in 2.588 / LTS 2.580.2, including an unauthenticated setup-wizard admin raceconfig.xml APIs, setup wizard, agent endpointsNone to low

With the plugin, the controller keeps no public address. The only traffic that arrives from outside is a webhook delivery, replayed by the plugin to the SCM endpoint you chose, so none of the endpoints in the table can be reached from the internet. Keep patching anyway; the full write-up is in Jenkins vulnerabilities exploited in the wild and the hardening checklist in Securing Jenkins. Each CVE link above goes to its Threat Watch record, with affected and fixed versions and a dated exploitation timeline.

Requirements

1. Install the plugin

From Manage Jenkins → Plugins → Available, search for Webhook Relay and install it.

To install a downloaded build manually, go to Manage Jenkins → Plugins → Advanced settings → Deploy Plugin and upload the webhook-relay.hpi file:

Deploy the Webhook Relay plugin

After restarting, the plugin shows up under Installed plugins:

Webhook Relay plugin installed

2. Configure the connection

Open Manage Jenkins → System and scroll to the Webhook Relay section:

  1. Enter your API Key and API Secret from the tokens page.
  2. Enter a Bucket name (for example jenkins-plugin). It can be an existing bucket or a new name — the plugin will create it for you.
  3. Pick your SCM Webhook Preset (GitHub, GitLab, Bitbucket or Generic Webhook Trigger).
  4. Tick Enable and click Save.

Configure the Webhook Relay plugin

When connected, Connection Status reads ✔ Subscribed. Use Test Connection to verify your credentials at any time.

3. Get your webhook URL

Click Get Webhook URL. The plugin resolves the bucket's public input URL (creating the bucket if it doesn't exist yet) and shows the value to paste into your repository:

Resolve the public webhook URL

Add it as a webhook in your repository settings:

SCMWhere to paste the URL
GitHubSettings → Webhooks → Add webhook → Payload URL (content type application/json)
GitLabSettings → Webhooks → URL
BitbucketRepository settings → Webhooks → Add webhook → URL

That's the only change in your SCM — you point the webhook at the Webhook Relay URL instead of at Jenkins directly.

4. Trigger a build

Configure the Jenkins job exactly as you would for a public Jenkins. For GitHub, that means a Pipeline or Freestyle job with a Git SCM and GitHub hook trigger for GITScm polling enabled (githubPush() in a declarative pipeline). For GitLab and Bitbucket, enable the corresponding hook trigger; for everything else, use the Generic Webhook Trigger.

Now push a commit. The webhook travels GitHub → bucket → plugin → /github-webhook/, and the build starts:

Build triggered by a GitHub push through Webhook Relay

5. Multibranch Pipelines

Multibranch Pipeline projects work too, but they are configured differently — a Multibranch Pipeline has no Build Triggers section. Branch and pull request jobs are created and triggered by the branch source (e.g. GitHub Branch Source) reacting to the webhook events the plugin delivers to the same endpoints.

The full setup — branch builds, automatic jobs for new branches and pull requests — has its own page: Jenkins Multibranch Pipelines.

6. Inspect deliveries

Open your bucket on my.webhookrelay.com/buckets. Each webhook is listed with its method, status and the response Jenkins returned — a sent status with response status 200 means Jenkins accepted the webhook.

Try it with Docker

The plugin repository ships a ready-to-run demo (Jenkins + Configuration as Code):

git clone https://github.com/jenkinsci/webhook-relay-plugin
cd webhook-relay-plugin
mvn -q clean package
docker compose -f demo/docker-compose.yml up --build   # Jenkins on http://localhost:8095

Then follow the steps above.

Prefer the CLI agent?

If you'd rather run a standalone forwarding agent instead of installing a plugin, see Jenkins and GitHub and Jenkins and Bitbucket, which use the relay CLI. The plugin and the CLI agent both deliver through buckets, so the logs and SCM setup are identical — the plugin just removes the separate agent process.

Frequently asked questions

Why shouldn't Jenkins be exposed to the internet just to receive webhooks?

Because exploited Jenkins vulnerabilities such as CVE-2024-23897 (CLI file read), CVE-2017-1000353 and CVE-2018-1000861 hit endpoints like /cli and Stapler URL routing, not the webhook path. With the Webhook Relay plugin, Jenkins has no public address and only webhook deliveries reach it, replayed to /github-webhook/ or your SCM endpoint.

Can Jenkins receive GitHub webhooks without a public IP?

Yes. Install the Webhook Relay Jenkins plugin, add your API token and bucket, and Jenkins subscribes to the bucket over an outbound WebSocket. GitHub posts to the bucket's public URL and the plugin replays each request to /github-webhook/ inside Jenkins. No inbound ports, reverse proxy or public IP address is required.

Does this work with GitLab and Bitbucket as well?

Yes. The plugin replays each webhook to the Jenkins endpoint you configure, so GitLab (/project/NAME) and Bitbucket (/bitbucket-hook/) work exactly like GitHub. Anything that can POST to an HTTPS URL — Gitea, Jira, your own tooling — can trigger Jenkins the same way.

Do I need to run a separate relay agent next to Jenkins?

No. The plugin runs inside Jenkins, so there is nothing else to install or keep alive. If you prefer, the relay CLI or Docker agent can forward to Jenkins from any machine on the same private network instead.

What happens if Jenkins is offline when a webhook arrives?

Webhook Relay stores the request, so the provider sees a successful response. With durable retries enabled, delivery is retried for up to 30 days, missed requests can be replayed when the plugin reconnects, and every request can be re-sent from the bucket's logs page.

Can I filter which events reach Jenkins?

Yes. Use forwarding rules or a transformation function on the bucket to only forward pushes to certain branches, pull-request events, or requests with a valid signature, so Jenkins never sees the rest.

Did this page help you?