Secure Automation with Forgejo: Deploy Keys + Signed Webhooks
Connect a self‑hosted Forgejo instance to CI/CD without broad PATs by using read‑only SSH deploy keys for code access and signed webhooks for push‑based event notifications. Follow the step‑by‑step guide, trade‑off analysis, and practical checklist to secure your automation pipeline.
28 Sept 2026, 23:16 UTC

Problem: How to connect a self‑hosted Forgejo instance to CI/CD without leaking credentials or polling Git
When you run your own Forgejo server you still need a way for external services—build servers, deployment pipelines, or custom bots—to read code and react to changes. The naive approach is to give the automation a personal access token (PAT) that has read or write rights to the whole instance. That token can be abused, hard to rotate, and usually grants more access than the automation actually needs.
Polling the Git repository for new commits is another option, but it wastes bandwidth, introduces latency, and is fragile if the server goes down.
Thesis: Use repository‑scoped SSH deploy keys for read access and signed webhooks for event notifications.
Deploy keys are SSH public keys that you attach to a single repository. They are read‑only unless you explicitly give write permission. Webhooks let Forgejo push JSON payloads to an HTTPS endpoint whenever an event (push, pull‑request, merge) occurs. Together they provide a minimal‑privilege, push‑based integration that is both secure and efficient.
Section 1 – Setting up a Read‑Only Deploy Key
1. Generate an SSH key pair on the automation host (ssh-keygen -t ed25519 -f /etc/forgejo/deploy-key -N ""). Keep the private key in a secure vault.
2. In the target repository’s settings → Deploy Keys, click Add Deploy Key. Paste the contents of deploy-key.pub and give it a name like CI‑Read‑Only. Do not enable write access.
3. On the automation host, clone the repository using the SSH URL that Forgejo provides:
git clone [contact removed]:team/repo.git
When the clone succeeds, the key is scoped correctly. If you try to push, Git will reject the operation, confirming that write access is not granted.
Section 2 – Configuring a Signed Webhook
Forgejo can POST events to an HTTPS endpoint. The payload is JSON and, if a secret is set, a signature header is added. The header name is X-Forgejo-Signature in recent releases, but some deployments may use X-Hub-Signature-256. Check your instance’s admin panel for the exact header.
To create the webhook via the UI:
- Go to Settings → Webhooks in the repository.
- Click Add Webhook, choose “Custom URL”.
- Enter the endpoint URL (e.g.,
https://ci.example.com/forgejo/webhook). - Check “Send push events” and any other events you need.
- Enter a secret string (e.g.,
my‑super‑secret). This will be used to compute an HMAC‑SHA256 signature. - Save.
Verify connectivity:
# In the webhook UI, click “Test Delivery”
# Open the “Recent Deliveries” page and inspect the payload.
# The request should include the signature header.
On the CI side, validate the signature before processing:
def verify_signature(request_body, header, secret):
import hmac, hashlib
expected = "sha256=" + hmac.new(secret.encode(), request_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header)
Once verified, enqueue a job keyed by the commit SHA. This guarantees idempotency: if the same event is delivered again (webhook retries), the job will not duplicate work.
Section 3 – Trade‑offs & Limitations
- Push‑based latency: Webhooks deliver events immediately, whereas polling can be delayed.
- Duplicate deliveries: Forgejo may retry failed deliveries up to a configurable limit. Your endpoint must handle duplicates.
- Endpoint availability: If the CI server is down, events are queued until the next retry. A missing event means your build may be out of date.
- Deploy key scope: Deploy keys are repository‑specific. If your automation needs to access multiple repos, you must create separate keys or use a PAT with limited scopes.
- Admin restrictions: Some Forgejo installations block outbound HTTPS or SSH traffic. Verify network policies before deploying.
Section 4 – Practical Checklist
- Generate and store a read‑only SSH key pair on the automation host.
- Add the public key as a deploy key in each target repository.
- Configure a webhook with a secret and verify the signature header.
- Implement idempotent job queuing keyed by commit SHA.
- Set up monitoring for webhook delivery failures and retry limits.
- Document key rotation procedures and revoke keys when a repository is deleted.
Actionable Takeaway
By combining deploy keys and signed webhooks you give your automation the exact permissions it needs, avoid storing broad PATs, and keep the integration push‑based for low latency. Start with a single repository, test the flow, then scale to multiple repos by adding more deploy keys. Remember to monitor webhook delivery and enforce signature validation to keep the system secure.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.