- Go 67.2%
- HTML 29.5%
- JavaScript 1.9%
- Just 0.7%
- Dockerfile 0.5%
- Other 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| .github | ||
| .vscode | ||
| cmd | ||
| config | ||
| internal | ||
| static | ||
| .env.example | ||
| .gitignore | ||
| .golangci.yml | ||
| CHANGELOG.md | ||
| CODEOWNERS | ||
| cog.toml | ||
| CONTRIBUTING.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| DONE-questions.md | ||
| go.mod | ||
| go.sum | ||
| Justfile | ||
| LICENSE | ||
| README-development.md | ||
| README.md | ||
| renovate.json | ||
| screenshot-logs.png | ||
| screenshot-main.png | ||
| USERS.md | ||
renovate-docker-operator
A standalone Docker-based operator for running Renovate against Forgejo/Gitea instances. Manages Renovate containers via Docker socket — no Kubernetes required.
Origin & Motivation
This project is a fork of mogenius/renovate-operator, adapted for environments that don't run Kubernetes.
Why does this exist?
-
Renovate CE/EE does not support Forgejo. The feature request (mend-io/renovate-ce-ee#173) was locked and closed without resolution. If you self-host Forgejo, Mend's commercial offering simply won't work.
-
The upstream renovate-operator is Kubernetes-only. It relies on CRDs, controller-runtime, and Kubernetes Jobs for container orchestration — a non-starter if your infrastructure is Docker Compose, Podman, or a single VM.
-
This project extracts the core logic into a standalone binary that talks directly to the Docker socket. Same features (priority queues, parallelism, webhooks, discovery, UI), zero Kubernetes dependencies, single ~20 MB binary + SQLite for state.
Credit: The Forgejo webhook handling, repository discovery agent, priority queue, and web UI patterns all originate from the excellent work by the mogenius team. Thank you for building this in the open.
Features
- Docker-native: No Kubernetes required. Manages Renovate containers via Docker socket.
- SQLite state: Lightweight persistence with WAL mode for concurrent access.
- Webhook-driven: Forgejo webhooks trigger immediate Renovate runs on PR/issue changes.
- Cron scheduling: Automatic discovery and execution on configurable cron schedules.
- Web UI: Dashboard showing job status, project runs, and streaming logs.
- OIDC auth (planned): Optional authentication via any OIDC provider.
RENOVATE_*w pass-through: AllRENOVATE_*env vars on the operator are injected into spawned containers.- Image caching: Configurable TTL avoids redundant Docker image pulls.
- Access logging: HTTP access log middleware with webhook auth enrichment.
- CSRF protection: Origin-based CSRF middleware on state-changing requests.
Quick Start
# 1. Clone
git clone https://git.h.oluflorenzen.de/finkregh/renovate-docker-operator.git
cd renovate-docker-operator
# 2. Copy and edit environment file
cp .env.example .env
# Edit .env — set RENOVATE_TOKEN at minimum
# 3. Start with Docker Compose
docker compose up -d
The UI is available at http://localhost:8081
Example: Running against Forgejo
cp .env.example .env
Edit .env:
ROP_PLATFORM_ENDPOINT=https://git.example.com
RENOVATE_TOKEN=<your-forgejo-token>
RENOVATE_PLATFORM=forgejo
Then:
docker compose up -d
# View logs
docker compose logs -f
# Open UI
open http://localhost:8081
See docker-compose.yml for all available options and .env.example for the full variable reference.
Configuration
All configuration is via environment variables:
| Variable | Default | Description |
|---|---|---|
RENOVATE_* |
All envvars beginning with RENOVATE_ are passed throug to spawned renovate containers |
|
RENOVATE_PLATFORM |
forgejo |
Platform type (forgejo, gitea, github, gitlab) — passed to containers |
RENOVATE_TOKEN |
(required) | Platform access token for Renovate |
ROP_PLATFORM_ENDPOINT |
(required) | Forgejo/Gitea instance URL |
ROP_IMAGE |
renovate/renovate:latest |
Docker image for Renovate |
ROP_CRON_SCHEDULE |
0 */4 * * * |
Cron expression for discovery+run cycles |
ROP_PARALLELISM |
2 |
Max concurrent Renovate containers |
ROP_SERVER_PORT |
8081 |
HTTP server port (UI + webhook + API) |
ROP_SQLITE_PATH |
/data/renovate.db |
Path to SQLite database |
ROP_CACHE_VOLUME |
renovate-cache |
Docker volume for Renovate cache |
ROP_CONTAINERBASE_CACHE_VOLUME |
renovate-containerbase-cache |
Docker volume for Containerbase tool caches (uv, nix, pip, cargo, gradle, m2, gem, …). Safe to delete; refilled on next run. |
ROP_CONTAINER_NETWORK |
(empty) | Docker network for Renovate containers |
ROP_IMAGE_PULL_POLICY |
if-not-present |
When to pull image (always, if-not-present, never) |
ROP_IMAGE_CACHE_TTL |
24h |
Duration to cache the pulled image (0 disables) |
ROP_JOB_TIMEOUT |
1800 |
Max runtime per Renovate container (seconds) |
ROP_SHUTDOWN_GRACE_PERIOD |
300 |
Grace period for stopping containers on shutdown (seconds) |
ROP_MAX_REQUEST_BODY |
2097152 |
Max webhook/API request body size in bytes (2 MiB) |
ROP_LOG_LEVEL |
info |
Log level (debug, info, warn, error) |
Any environment variable prefixed RENOVATE_* on the operator process is passed through 1:1 to spawned Renovate containers.
Cache volumes
The operator mounts two Docker volumes into every spawned Renovate container:
| Volume (env var) | Container path | What it holds |
|---|---|---|
renovate-cache (ROP_CACHE_VOLUME) |
/tmp/renovate |
Renovate's own base/cache dir — repo checkouts, repos.json, and the manager caches Renovate itself wires here via ensureCacheDir (others/{go,npm,pnpm,yarn,berry}). |
renovate-containerbase-cache (ROP_CONTAINERBASE_CACHE_VOLUME) |
/tmp/containerbase/cache |
Containerbase-managed tool caches: uv, pip, poetry, nix (via the baked NIX_STORE_DIR, so the public binary substituter stays valid), cargo, gradle, m2, gem, nuget, sbt, cocoapods, conan, dart, flutter, hex, mix, and $HOME/.cache/* in general (the image symlinks /home/ubuntu/.cache into this tree). |
Podman/Docker auto-seeds an empty named volume from the image's baked
skeleton on first mount, preserving the root:root drwxrwxr-x
permissions Containerbase requires. Both volumes are safe to delete to
reclaim disk — Renovate refills them on the next run.
GID 0 requirement. Child Renovate containers run as 12021:0 (UID
12021, GID 0). Containerbase's install/prep steps write into
/tmp/containerbase/cache as root:root with group-writable perms, so
the non-root user needs the root group to write there. Do not override
this with a custom --user flag; it will silently break tool caching
and you'll see Permission denied errors for uv, nix, and friends.
Reclaiming disk. To wipe caches:
docker volume rm renovate-cache renovate-containerbase-cache
Or, targeted — e.g. only the large containerbase volume:
docker volume rm renovate-containerbase-cache
Both are recreated automatically on the next Renovate run.
Webhook Configuration
| Variable | Default | Description |
|---|---|---|
ROP_WEBHOOK_ENABLED |
true |
Enable webhook endpoint |
ROP_WEBHOOK_SECRET |
(auto-generated) | HMAC secret for webhook validation, needs to be set in providers config (optional — auto-generated on first startup, comma-separated for rotation) |
Configure your Forgejo instance to send webhooks to:
http://renovate-operator:8081/webhook/v1/forgejo?job=default
Events to enable: Push, Issues (edited), and Pull Requests (edited, closed, reopened).
System Webhook Setup
For Forgejo instances where you want all repositories covered automatically, use a system webhook (Site Administration → Webhooks → Add Webhook → Forgejo):
- Target URL:
http://renovate-operator:8081/webhook/v1/forgejo?job=default - Content Type:
application/json - Secret: Your HMAC secret (retrieve from the operator — see below). This provides HMAC-SHA256 signature authentication via
X-Forgejo-Signature. - Branch Filter:
main(or{main,master}to match multiple default branches). This filter is applied server-side by Forgejo, so only pushes to matching branches are delivered. - Events: Select "Custom Events", then enable:
- Push — triggers Renovate on code changes to the default branch
- Pull Request (Modification) — triggers on PR edits/close/reopen for Renovate checkbox interactions
- Issues enables Renovate Dependency Dashboard checkbox interactions
Note
: The branch filter is applied server-side by Forgejo before delivery. Tag pushes and branch deletions are also filtered out by the operator as defense-in-depth.
Generating the Webhook Secret
On first startup, the operator auto-generates a random 40-character HMAC secret and stores it in the database. The full secret is logged on first generation. Retrieve it later with:
sqlite3 data/renovate.db "SELECT value FROM settings WHERE key='webhook_secret'"
Paste this value into your Forgejo webhook's Secret field.
To override with a custom secret (e.g., for infrastructure-as-code):
ROP_WEBHOOK_SECRET=$(openssl rand -hex 20)
When ROP_WEBHOOK_SECRET is set, it takes precedence over the auto-generated value and is stored in the sqlite database. So starting once with the envvar is enough for e.g. secret rotation.
Authentication (Planned)
OIDC authentication is not yet implemented but is planned. The following environment variables will be supported:
| Variable | Default | Description |
|---|---|---|
ROP_OIDC_ISSUER_URL |
(empty) | OIDC provider URL (leave empty for no-auth) |
ROP_OIDC_CLIENT_ID |
(empty) | OAuth2 client ID |
ROP_OIDC_CLIENT_SECRET |
(empty) | OAuth2 client secret |
ROP_OIDC_REDIRECT_URL |
(empty) | OAuth2 redirect URL |
ROP_SESSION_SECRET |
(auto-generated) | AES key for session cookies |
Tip: When implemented, you'll be able to use Forgejo itself as your OIDC provider! Forgejo 1.22+ has built-in OAuth2 provider support.
Scheduling
| Variable | Default | Description |
|---|---|---|
ROP_CRON_SCHEDULE |
0 */4 * * * |
Cron expression for discovery+run cycles |
ROP_CRON_SKIP_DISCOVERY |
false |
Skip discovery on cron (only run known projects) |
Discovery Filters
| Variable | Default | Description |
|---|---|---|
ROP_DISCOVERY_FILTERS |
(empty) | Comma-separated repo patterns (e.g., org/*,user/repo-*) |
ROP_DISCOVER_TOPICS |
(empty) | Comma-separated topics to filter by |
ROP_SKIP_FORKS |
false |
Skip forked repositories |
Resilience & Metrics
The operator includes a rapid-fail circuit breaker that protects against cascading failures when the underlying environment is unhealthy (e.g., invalid token, unreachable registry). Per-project exponential backoff prevents a single broken repository from monopolising dispatch slots. Operators can issue a manual bypass for individual projects, and the system exposes Prometheus metrics on /metrics for alerting and dashboards.
Configuration
| Variable | Default | Description |
|---|---|---|
ROP_RAPID_FAIL_THRESHOLD |
10 |
Number of rapid failures within the window to trip the breaker |
ROP_RAPID_FAIL_WINDOW |
5m |
Sliding window for counting rapid failures |
ROP_FAILURE_MIN_RUNTIME |
30s |
Containers exiting before this duration are classified as rapid failures |
ROP_BACKOFF_BASE |
30s |
Base delay for per-project exponential backoff |
ROP_BACKOFF_MAX |
30m |
Maximum per-project backoff cap |
ROP_REPLAY_QUEUE_CAP |
10000 |
Maximum queued webhook events when the breaker is open |
ROP_METRICS_PROJECT_LABEL |
all |
Project label cardinality on metrics (all, breaker, off) |
API Surface
GET /api/v1/breaker/state— current breaker snapshot (state, per-project backoffs, replay queue depth)POST /api/v1/breaker/reset— full reset: closes breaker, clears all backoffs, drains replay queuePOST /api/v1/breaker/bypass/{org/repo}— single-shot manual override for one projectGET /metrics— Prometheus text exposition format
Webhook Behaviour When Breaker Is Open
When the breaker is open, webhooks return 202 Accepted and their events are queued; the queue drains automatically on reset (up to ROP_REPLAY_QUEUE_CAP, defaults 10 000, beyond which a 503 is returned).
Ops runbook: See
unipi/docs/runbooks/breaker.mdfor diagnosis and recovery procedures.
API Endpoints
| Method | Path | Description |
|---|---|---|
GET |
/healthz |
Health check |
GET |
/api/v1/version |
Server version |
GET |
/api/v1/renovatejobs |
List all jobs with project statuses |
POST |
/api/v1/renovate |
Trigger Renovate for a project |
POST |
/api/v1/renovate/all |
Trigger all projects |
POST |
/api/v1/renovate/cancel |
Cancel a running project |
GET |
/api/v1/logs?renovate=X&project=Y |
Stream logs (SSE) |
POST |
/api/v1/discovery/start |
Trigger discovery |
POST |
/api/v1/executionOptions |
Update debug mode |
POST |
/webhook/v1/forgejo?job=X |
Forgejo webhook receiver |
POST |
/webhook/v1/schedule?project=X&job=Y |
Manual schedule trigger |
Architecture
┌─────────────────────────────────────────┐
│ renovate-docker-operator │
├─────────────────────────────────────────┤
│ HTTP Server (port 8081) │
│ ├── /webhook/v1/* (Forgejo hooks) │
│ ├── /api/v1/* (REST API) │
│ ├── /healthz (health) │
│ └── /* (static UI) │
├─────────────────────────────────────────┤
│ Scheduler (robfig/cron) │
│ └── Discovery → Schedule → Dispatch │
├─────────────────────────────────────────┤
│ Docker Executor │
│ ├── Container create/start/wait/logs │
│ ├── Priority queue dispatch │
│ └── Docker Events API (exit detect) │
├─────────────────────────────────────────┤
│ SQLite State Store (WAL mode) │
│ └── Jobs, Projects, Logs, Webhooks │
└─────────────────────────────────────────┘
│
▼ Docker Socket
┌─────────────────────────────────────────┐
│ renovate/renovate containers │
│ (one per project, max parallelism) │
└─────────────────────────────────────────┘
Development
For development documentation — project structure, coding conventions, architectural decisions, and build/test instructions — see README-development.md.
Differences from Upstream
| Aspect | mogenius/renovate-operator | renovate-docker-operator |
|---|---|---|
| Runtime | Kubernetes (CRDs, controller-runtime) | Docker (socket API) |
| State store | Kubernetes CRDs + etcd | SQLite (WAL mode) |
| Container orchestration | Kubernetes Jobs | Docker containers |
| Configuration | CRDs + ConfigMaps | Environment variables |
| Image size | ~50 MB (requires K8s cluster) | ~20 MB standalone binary |
| Dependencies | controller-runtime, client-go | Docker SDK, modernc.org/sqlite |
| Auth | OIDC + GitHub OAuth | Planned (OIDC + no-auth; not yet implemented) |
| Platforms | Forgejo, Gitea, GitHub, GitLab | Same |
| Env var prefix | Mixed | ROP_* (operator) / RENOVATE_* (pass-through) |
| Renovate features | Priority queue, parallelism, webhooks, UI | Same |
| Deployment | Helm chart, K8s cluster | docker-compose or bare binary |
We share the same Forgejo webhook logic, discovery agent, and UI patterns as upstream. This means improvements from mogenius/renovate-operator can be cherry-picked into this project when relevant.
Related Projects
- mogenius/renovate-operator — Original Kubernetes-based operator (upstream)
- renovatebot/renovate — Renovate itself
- mend-io/renovate-ce-ee — Official Mend Renovate CE/EE (no Forgejo support)
License
MIT — see LICENSE.

