A comprehensive guide for AI agents and developers to understand and navigate the Keel codebase.
If you're new to this codebase, read these files in order:
types/types.go- Core domain types (Repository, Event, Policy, TriggerType)cmd/keel/main.go- Application entry point, shows how everything connectsprovider/provider.go- Provider interface definitiontrigger/poll/watcher.go- Example trigger implementation
Keel is a Kubernetes deployment automation tool written in Go. It watches container registries for new image versions and automatically updates Kubernetes deployments based on configured policies.
┌─────────────────────────────────────────────────────────────────────────┐
│ TRIGGERS │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────────┐ │
│ │ Poll Trigger │ │ PubSub (GCR) │ │ Webhooks (DockerHub, etc.) │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────────┬───────────────┘ │
└─────────┼─────────────────┼──────────────────────────┼──────────────────┘
│ │ │
└─────────────────┴────────────┬─────────────┘
│
▼
┌─────────────────┐
│ Event │
│ (Repository + │
│ new version) │
└────────┬────────┘
│
┌───────────────────┴───────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Kubernetes │ │ Helm3 │
│ Provider │ │ Provider │
└────────┬────────┘ └────────┬────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Update Deployment│ │ Update Release │
│ (if policy match)│ │ (if policy match)│
└─────────────────┘ └─────────────────┘
| Directory | Purpose | Key Files |
|---|---|---|
cmd/keel/ |
Entry point - Application startup, wiring | main.go |
provider/ |
Deployment handlers - Update K8s/Helm resources | provider.go, kubernetes/, helm3/ |
trigger/ |
Event sources - Detect new image versions | poll/, pubsub/ |
pkg/http/ |
HTTP server + webhooks - REST API, registry webhooks | http.go, *_webhook_trigger.go |
types/ |
Domain types - Core data structures | types.go |
internal/policy/ |
Version matching - Semver, glob, force, regexp | policy.go, semver.go |
extension/ |
Plugins - Notifications, credentials helpers | notification/, credentialshelper/ |
approvals/ |
Approval workflow - Manual approval before updates | approvals.go |
bot/ |
Chat bots - Slack/HipChat for approvals | bot.go, slack/, hipchat/ |
registry/ |
Registry client - Docker registry API | registry.go |
secrets/ |
K8s secrets - Extract registry credentials | secrets.go |
ui/ |
Web dashboard - Vue.js frontend | src/ |
pkg/store/ |
Persistence - SQLite database | sql/ |
pkg/auth/ |
Authentication - Basic auth, JWT | |
internal/k8s/ |
K8s utilities - Watchers, resource cache | |
chart/ |
Helm chart - Deploy Keel itself | |
constants/ |
Environment variables - Config constants | |
version/ |
Build info - Version, revision | |
util/ |
Utilities - Image parsing, etc. |
Providers handle deployment updates for different platforms. They implement the Provider interface:
// provider/provider.go
type Provider interface {
Submit(event types.Event) error // Process an update event
TrackedImages() ([]*types.TrackedImage, error) // List monitored images
GetName() string
Stop()
}Available providers:
provider/kubernetes/- Native Kubernetes Deployments, StatefulSets, DaemonSets, CronJobsprovider/helm3/- Helm v3 releases (enabled viaHELM3_PROVIDER=true)
Triggers detect new image versions and emit Event objects:
// types/types.go
type Event struct {
Repository Repository // Image info (host, name, tag, digest)
CreatedAt time.Time
TriggerName string // "poll", "pubsub", "webhook", etc.
}Available triggers:
trigger/poll/- Periodically polls registries for new tagstrigger/pubsub/- Google Cloud Pub/Sub for GCR eventspkg/http/*_webhook_trigger.go- Webhooks from DockerHub, Azure, GitHub, Harbor, Quay, JFrog
Policies determine which version updates are allowed. Set via keel.sh/policy annotation:
// internal/policy/policy.go - Policy types
type PolicyType int
const (
PolicyTypeNone PolicyType = iota
PolicyTypeSemver // major, minor, patch, all
PolicyTypeForce // always update (for :latest)
PolicyTypeGlob // glob pattern matching
PolicyTypeRegexp // regex pattern matching
)Policy examples:
keel.sh/policy: major- Allow major version bumps (1.x.x → 2.x.x)keel.sh/policy: minor- Allow minor bumps (1.1.x → 1.2.x)keel.sh/policy: patch- Allow patch bumps only (1.1.1 → 1.1.2)keel.sh/policy: force- Always update (for mutable tags likelatest)keel.sh/policy: glob:release-*- Match glob patterns
Extensible notification system using sender registration pattern:
// extension/notification/notification.go
func RegisterSender(name string, s Sender) { ... }Available senders: Slack, Teams, Discord, Mattermost, HipChat, Mail, Webhook, Auditor
Notifications are registered via blank imports in cmd/keel/main.go:
_ "github.com/keel-hq/keel/extension/notification/slack"Manual approval workflow before updates proceed:
// approvals/approvals.go
type Manager interface {
Create(r *types.Approval) error
Approve(id, voter string) (*types.Approval, error)
Reject(id string) (*types.Approval, error)
// ...
}Set via keel.sh/approvals: "2" annotation to require N approvals.
- Trigger detects new version → Creates
types.Event - Event submitted to Providers →
provider.Submit(event) - Provider checks policies →
internal/policy/evaluates if update allowed - Approval check → If approvals required, waits for manual approval
- Deployment updated → Provider patches K8s resource or Helm release
- Notifications sent → Slack/webhook/etc. notified of update
| Annotation | Purpose | Example |
|---|---|---|
keel.sh/policy |
Update policy | minor, patch, force, glob:v* |
keel.sh/trigger |
Trigger type | poll (default: webhooks) |
keel.sh/pollSchedule |
Poll frequency | @every 5m |
keel.sh/approvals |
Required approvals | 2 |
keel.sh/approvalDeadline |
Approval timeout (hours) | 24 |
keel.sh/notify |
Override notification channel | #deployments |
keel.sh/matchTag |
Force tag matching | true |
keel.sh/matchPreRelease |
Match pre-release versions | true |
keel.sh/digest |
Track by digest (internal) | SHA256 digest |
keel.sh/imagePullSecret |
Registry credentials secret | my-registry-secret |
keel.sh/releaseNotes |
Release notes URL | https://... |
keel.sh/initContainers |
Track init containers | true |
| Variable | Purpose | Default |
|---|---|---|
PUBSUB |
Enable GCR Pub/Sub trigger | (disabled) |
POLL |
Enable/disable poll trigger | 1 (enabled) |
PROJECT_ID |
GCP project for Pub/Sub | |
HELM3_PROVIDER |
Enable Helm3 provider | false |
DEBUG |
Enable debug logging | false |
NOTIFICATION_LEVEL |
Min notification level | info |
BASIC_AUTH_USER |
HTTP basic auth username | |
BASIC_AUTH_PASSWORD |
HTTP basic auth password | |
AUTHENTICATED_WEBHOOKS |
Require auth for webhooks | false |
DOCKER_REGISTRY_CFG |
Default registry credentials | |
XDG_DATA_HOME |
Data directory (SQLite) | /data |
UI_DIR |
Web UI static files | www |
KUBERNETES_CONFIG |
Kubeconfig path | ~/.kube/config |
POLL_DEFAULTSCHEDULE |
Default poll interval | @every 1m |
- Create
extension/notification/mynotifier/mynotifier.go - Implement
notification.Senderinterface - Register via
init():func init() { notification.RegisterSender("mynotifier", &sender{}) }
- Add blank import in
cmd/keel/main.go:_ "github.com/keel-hq/keel/extension/notification/mynotifier"
- Create
pkg/http/myregistry_webhook_trigger.go - Parse the webhook payload, extract repository/tag info
- Create
types.Eventand callproviders.Submit(event) - Register route in
pkg/http/http.go
- Create
provider/myprovider/ - Implement
provider.Providerinterface - Initialize in
cmd/keel/main.gosetupProviders()
- Create
extension/credentialshelper/myhelper/ - Implement
credentialshelper.CredentialsHelperinterface - Register via
init()and blank import inmain.go
# Build
make build
# Run locally (outside cluster)
make run
# Run tests
make test
# Build Docker image
make image| Task | Where to Look |
|---|---|
| Add new webhook support | pkg/http/*_webhook_trigger.go |
| Change version matching logic | internal/policy/ |
| Modify K8s update behavior | provider/kubernetes/ |
| Add notification channel | extension/notification/ |
| Change polling behavior | trigger/poll/ |
| Modify approval workflow | approvals/, bot/ |
| Add registry authentication | extension/credentialshelper/ |
| Parse image references | util/ |
| HTTP API endpoints | pkg/http/ |
# Unit tests
make test
# E2E tests (requires running cluster)
make e2eTest files follow Go convention: *_test.go alongside source files.
The web dashboard is a Vue.js application in ui/:
cd ui
yarn install
yarn run serve # Development
yarn run build # Production buildBuilt assets go to ui/dist/, served by Keel's HTTP server.