Webhook
Webhook lets you run lightweight webhook server for running shell commands entirely on your own server.
Open-source webhook receiver, honestly reviewed. What you actually get when you swap GitHub Actions or a full CI stack for a 7 MB binary.
TL;DR
- What it is: A lightweight Go daemon that listens for HTTP requests and executes shell commands on your server — nothing more, nothing less [README].
- Who it’s for: Developers who need a dead-simple way to trigger deploys, builds, or server scripts from GitHub, GitLab, Slack, or any other service that can send an HTTP POST [1][2][3].
- Cost: $0. MIT-licensed, single binary, no cloud dependency, no per-execution pricing [README].
- Key strength: Brutally simple. One JSON or YAML file, one binary, one open port — and any shell script becomes a remotely callable HTTP endpoint in under 10 minutes [1][2].
- Key weakness: This is a script runner, not a workflow engine. It has no UI, no execution history, no retry logic, no alerting, and no built-in secret management. If your script fails silently, you may not know for hours [2][3].
What is Webhook
Webhook (adnanh/webhook) is a single-purpose Go tool: it exposes HTTP endpoints on your server that, when hit, execute a command you configured. That’s the entire product. The README states the design philosophy plainly — it aims to do nothing more than receive the request, parse headers and payload, check configured rules, and pass arguments to your command [README]. Everything else is your problem.
The canonical use case in the README is auto-deployment: you push to your GitHub master branch, GitHub sends a POST to your server, webhook receives it, runs /var/scripts/redeploy.sh. No CI/CD pipeline, no GitHub Actions, no cloud services involved.
The project is maintained by Adnan Hajdarević and sits at 11,674 GitHub stars with 867 forks. It’s been around long enough to have packages in Ubuntu (17.04+), Debian (“stretch”+), FreeBSD, and the Snap store — which tells you this tool has a stable, settled community around it [README]. It’s not a startup, it’s not a company, it’s an infrastructure utility that’s been quietly running on thousands of servers for years.
What it is not: it’s not Zapier, it’s not n8n, it’s not a no-code tool. There is no dashboard, no visual flow builder, no managed SaaS tier. If you’re a non-technical founder who wants to automate something without touching a terminal, this is the wrong tool. But if you’re a developer who wants to replace a 200-line Python Flask app with a config file and a binary, this is exactly right.
Why people choose it
The three third-party articles we reviewed all tell the same story: people find webhook when they need a lightweight deploy trigger and don’t want to pay for or maintain a heavier solution.
Will Browning [2] describes the problem that drives most people here: he needed automatic deployment whenever he pushed to GitHub, but didn’t want the overhead of a full CI system. His solution was the adnanh webhook daemon plus a bash script. He notes it’s “really simply to get set up with JSON configuration files” — and his tutorial walks through a realistic production setup including supervisor for process management and HMAC secret verification.
The dev.to article by Sylvain Lesage [3] covers the same territory for a Jekyll blog, adding an important security detail: he uses payload-hash-sha1 in the hook configuration to verify that incoming requests are actually from GitHub before running the deploy script. He also addresses running the daemon as a non-root user — a real operational concern that gets glossed over in simpler tutorials.
The netcup.com tutorial [1] is more basic but covers the optional flags include-command-output-in-response and include-command-output-in-response-on-error, which control whether the HTTP response body contains the stdout/stderr of your script. That’s useful for debugging — you can test your hook with curl and see exactly what your script printed.
The common thread: webhook wins over alternatives when you want the smallest possible surface area. No database, no web framework, no authentication system to maintain — just a config file and a binary that runs as a service.
Features
Core mechanics:
- HTTP endpoint per hook, served on port 9000 by default (configurable) [README]
- JSON or YAML configuration files — both fully supported [README]
- Pass HTTP request data (headers, payload, query params) to your script as command-line arguments or environment variables [README]
- Rule-based triggering: you can require specific header values, payload fields, or HMAC signatures before a hook fires [README][3]
- HTTPS support via TLS configuration [README]
- Multipart form data parsing [README]
- Go templates for dynamic argument construction [README]
- Hot reload of hook configuration without restarting the daemon [README]
- CORS header configuration [README]
- Run behind a reverse proxy (nginx, Caddy) [README]
- Systemd integration [README]
- Privilege dropping (run as non-root after binding to low ports) [3]
What it passes to your script:
- Any header value by name
- JSON payload fields (nested path syntax supported)
- Query string parameters
- The raw request body
- Environment variables
What it explicitly doesn’t do:
- Store execution history
- Retry failed hooks
- Send notifications on failure
- Provide a web UI
- Queue concurrent executions (by default)
- Manage secrets (you handle that in your config or environment)
The README’s design philosophy — “everything else is the responsibility of the command’s author” — is honest and accurate. Webhook is glue. What happens inside your script is entirely up to you.
Pricing: SaaS vs self-hosted math
There is no SaaS version of webhook. It’s a binary you download and run. Pricing discussion is therefore short:
Webhook itself: $0. MIT license, no telemetry, no license server, no “community edition” with features locked behind a paywall [README].
Infrastructure to run it:
- A VPS running anything from Ubuntu 17.04 to the latest Debian: $4–6/month on Hetzner or Contabo
- Or run it on an existing server you already have: $0 additional
Comparison context — what people are replacing:
- GitHub Actions: 2,000 free minutes/month, then $0.008/minute. For teams doing frequent deploys, this adds up. More importantly, GitHub Actions requires your build environment to be declarable as YAML workflows — webhook doesn’t care, it just runs your script.
- Jenkins: free software, but a Java application that needs a JVM, a persistent server, and ongoing maintenance. Overkill for “run this deploy script when GitHub pushes.”
- Managed webhook services like Hookdeck (mentioned in the README as an alternative): paid SaaS with queuing, retries, and observability — more features, but you’re paying a monthly bill for what webhook does for free [README].
The math is trivial: if you’re already paying for a VPS to host your application, adding webhook to that server costs nothing. You’re just running another process.
Deployment reality check
Installation options (all straightforward):
sudo apt-get install webhookon Ubuntu/Debian [README]pkg install webhookon FreeBSD [README]- Snap store [README]
- Download prebuilt binary from GitHub Releases [README]
- Build from source:
go build github.com/adnanh/webhook(requires Go 1.21+) [README]
Getting to a working hook:
- Write your shell script, make it executable
- Create
hooks.jsonwith your hook definition - Run
webhook -hooks hooks.json -verbose - Hit
http://yourserver:9000/hooks/your-hook-id
That’s it. The netcup tutorial [1] estimates this takes a few minutes for someone who’s done it before. Call it 20–30 minutes for a first-timer including testing.
For production use, you need a few more steps:
Running webhook as a persistent service requires either systemd (the README documents this) or a process supervisor like supervisord. Will Browning [2] covers the supervisord path, including the non-obvious issue of environment variables — scripts run by webhook inherit a minimal environment, not your full shell environment, which can break scripts that depend on PATH, HOME, or language-specific paths. His solution: pass environment explicitly or use bash -c to source your profile.
Security configuration matters. At minimum you want:
- HMAC secret verification so only legitimate senders can trigger your hooks [2][3]
- Running webhook as a non-root user [3]
- Reverse proxy (nginx or Caddy) for HTTPS if you’re exposing this to the internet [README]
What can go wrong:
- Silent failures. If your script exits non-zero, webhook logs it locally but doesn’t alert you anywhere. You need to wire up your own alerting inside the script [2].
- Concurrent executions. If two requests come in simultaneously, webhook will try to run your script twice concurrently. For deploy scripts that do
git pull+npm build, this causes race conditions. You need a lock file in your script or thepass-environment-to-command+ mutex approach [2]. - The version in Ubuntu/Debian repos may lag behind the GitHub release. If you need recent features, install from the prebuilt binaries or build from source [README].
- No secret management built in. Your HMAC secrets live in
hooks.jsonin plaintext, or you read them from environment variables (which is the right approach, but you have to implement it yourself).
Realistic time to a working, production-grade setup: 45–90 minutes for a developer familiar with Linux servers. This includes writing your script, configuring the hook with HMAC verification, setting up systemd, and testing with curl. Non-technical users should not attempt this without help — there is no guided setup, no UI, and no error messages designed for beginners.
Pros and cons
Pros
- Single binary, zero dependencies. No runtime, no database, no web framework. Drop the binary on any Linux server and it runs [README].
- MIT licensed. No restrictions, no “fair-code” limitations, no commercial license required to use it in your products [README].
- Stable and battle-tested. 11,674 stars, packages in Ubuntu/Debian/FreeBSD repos, 549 commits over multiple years. This is not experimental software [README].
- Genuinely minimal. It does one thing. The attack surface is small, the failure modes are predictable, and there’s nothing to “learn” beyond the hook definition format [README][1].
- No execution limits. No per-request pricing, no throttling, no “you’ve hit your monthly quota” emails. Run it as hard as you want [README].
- Rule-based triggering. You can require specific headers, payload contents, or HMAC signatures before a hook fires — good enough security for most deploy use cases [README][3].
- Multiple install paths. apt, snap, FreeBSD pkg, prebuilt binaries, build from source — fits into whatever your server already has [README].
Cons
- No observability built in. No execution log, no success/failure history, no alerting. You find out your deploy hook broke when the deployment silently failed three hours ago [2].
- No retry logic. If the webhook fires and your script fails (network hiccup, file lock, race condition), nothing retries automatically [2].
- Concurrent execution is your problem. Simultaneous requests trigger simultaneous script executions. For stateful operations like deploys, this is a real issue you have to solve in your script [2].
- Secrets management is primitive. HMAC secrets go in your config file or environment variables — there’s no vault integration, no secret rotation, no audit trail [README][3].
- No UI whatsoever. For developers: fine. For anyone who isn’t comfortable editing JSON files on a Linux server: a hard stop.
- Community package may be outdated. The Ubuntu/Debian apt package trails the GitHub release. You may need to install manually to get current features [README].
- Docs are GitHub README + a docs/ folder. The documentation is functional but not polished. You’ll be reading example config files and GitHub issues for edge cases.
Who should use this / who shouldn’t
Use webhook if:
- You need a deploy trigger that fires when GitHub pushes to a branch, and you don’t want to add GitHub Actions overhead or pay for a CI service.
- You want to expose a shell script as an HTTP endpoint for internal tooling — a
/hooks/clear-cacheor/hooks/restart-serviceendpoint that your monitoring system can call. - You’re connecting Slack slash commands or Mattermost outgoing webhooks to server actions.
- You already know your way around a Linux server and you want the smallest possible footprint for this problem.
- You want to replace a hand-rolled Python/Node.js webhook receiver with something battle-tested and maintained.
Skip it if:
- You need execution history or any kind of observability. Reach for something with a built-in UI — n8n, Woodpecker CI, or even GitHub Actions.
- You’re a non-technical founder who wants to automate business workflows. This tool is for infrastructure automation by developers, not no-code business automation.
- You need retry logic, dead-letter queues, or delivery guarantees. Hookdeck or a proper message queue is the right answer.
- Your hooks need to orchestrate multiple services or have conditional branching. That’s a workflow engine problem, not a script runner problem.
- You need detailed audit logs of who triggered what and when.
Alternatives worth considering
- GitHub Actions / GitLab CI — if your trigger is always a code push, the CI system that’s already built into your repo handles this without a separate server process. The tradeoff is you need YAML workflow files and you’re tied to the hosted CI platform.
- Woodpecker CI — self-hosted CI/CD with a proper UI, execution history, and pipeline support. More setup, more features, still free and open source. For teams doing more than simple deploys.
- Gitea + built-in webhooks — if you’re already self-hosting your git, Gitea has webhook support baked in without a separate service.
- n8n — if you need workflow logic (conditions, retries, multiple steps, a UI to configure it) rather than just “HTTP request → run script.” n8n’s webhook trigger is one piece of a full automation platform.
- Hookdeck (mentioned in the webhook README) — managed SaaS with queuing, retries, request inspection, and replay. You pay for those features with a monthly bill and a cloud dependency, but you get real observability.
- Pipedream — developer-focused automation platform with a free tier. Managed, but more featureful than webhook for complex trigger/action scenarios.
- Custom Flask/Express endpoint — what webhook replaces. If you’ve ever written a 50-line Python script that listens for GitHub payloads and runs a deploy, webhook is the maintained, configurable version of that.
For pure CD (continuous deployment) triggered by git push, the realistic choice is webhook vs. your CI system’s built-in webhooks. If your CI is GitHub Actions or GitLab CI, you probably don’t need this. If you’re self-hosted everywhere and want zero cloud dependencies, webhook makes sense.
Bottom line
Webhook is the right tool for exactly one job: routing an incoming HTTP request to a shell script on your server, with configurable rules and authentication. It does that job reliably, with a tiny footprint, under an MIT license, on any Linux server you already have. The 11K stars aren’t for flashy features — they’re for doing a dull, necessary thing correctly for years without drama.
The limitations are real and worth taking seriously: no execution history, no retries, no UI, concurrent execution is your problem. If you need any of those things, webhook is not the answer. But if you’ve been running a hand-rolled deploy script triggered by a curl call in a GitHub Actions step, or you wrote a 60-line Flask app just to receive webhook payloads — this replaces that with a battle-tested binary and a JSON config file. That’s the deal, and it’s a good one.
If self-hosting the infrastructure itself is the blocker, that’s the kind of one-time setup that upready.dev deploys for clients.
Sources
- Patrick Weber, netcup community — “Installing and Configuring a Webhook” (June 11, 2021). https://community.netcup.com/en/tutorials/how-to-install-and-configure-webhook-to-trigger-a-shell-script
- Will Browning — “Setting up Automatic Deployment and Builds Using Webhooks” (June 26, 2018, updated September 9, 2019). https://willbrowning.me/setting-up-automatic-deployment-and-builds-using-webhooks/
- Sylvain Lesage, dev.to — “Using Webhooks to Update a Self-Hosted Jekyll Blog” (August 26, 2019). https://dev.to/severo/using-webhooks-to-update-a-self-hosted-jekyll-blog-59al
Primary sources:
- GitHub repository and README: https://github.com/adnanh/webhook (11,674 stars, MIT license)
Features
Integrations & APIs
- Webhooks
Compare Webhook
Related Automation & Workflow Tools
View all 122 →n8n
180KOpen-source-ish workflow automation for people who write code and people who don't — the 180K-star platform technical teams actually adopt.
Langflow
146KVisual platform for building AI agents and MCP servers with drag-and-drop components, Python customization, and support for any LLM.
Dify
133KOpen-source platform for building production-ready agentic workflows, RAG pipelines, and AI applications with a visual builder and no-code approach.
Browser Use
81KMake websites accessible for AI agents — automate browsing, extraction, testing, and monitoring in natural language with Playwright and LLMs.
Ansible
68KThe most popular open-source IT automation engine — automate provisioning, configuration management, application deployment, and orchestration using simple YAML playbooks over SSH.
openpilot
60KOpen-source driver assistance system from comma.ai that brings adaptive cruise control and lane centering to 275+ supported car models.