A Kubernetes controller for managing application rollouts with support for health checks, gates, and bake time.
- Health Check Integration: Monitor application health during rollouts
- Rollout Gates: Control deployment progression with custom gates
- Bake Time: Wait for a specified duration before considering a deployment successful
- Multiple Rollout Support: Kustomizations can be managed by multiple rollouts using rollout-specific annotations
- Time-Based Scheduling: Control when deployments can occur using RolloutSchedule resources
Kustomizations can be managed by multiple rollouts simultaneously using rollout-specific annotations. Each rollout has its own annotation, preventing conflicts between different rollouts.
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app
annotations:
rollout.kuberik.com/substitute.frontend_version.from: "frontend-rollout"
rollout.kuberik.com/substitute.backend_version.from: "backend-rollout"
rollout.kuberik.com/substitute.api_version.from: "api-rollout"
spec:
# ... other spec fields
postBuild:
substitute:
frontend_version: "1.0.0" # Managed by frontend-rollout
backend_version: "2.0.0" # Managed by backend-rollout
api_version: "3.0.0" # Managed by api-rolloutIn this example:
frontend-rolloutmanages thefrontend_versionsubstitutebackend-rolloutmanages thebackend_versionsubstituteapi-rolloutmanages theapi_versionsubstitute
OCIRepositories use a simpler approach since they only have one modifiable field (tag). Each OCIRepository can be managed by a single rollout.
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: my-image-repo
annotations:
rollout.kuberik.com/rollout: "my-app-rollout"
spec:
# ... other spec fields
reference:
tag: "latest" # This will be updated by the referenced rollout- Kustomizations:
rollout.kuberik.com/substitute.<variable>.from: <rollout-name> - OCIRepositories:
rollout.kuberik.com/rollout
Each rollout can manage its own substitute in Kustomizations, while OCIRepositories are managed by a single rollout.
RolloutSchedule and ClusterRolloutSchedule resources enable time-based control over when rollouts can be deployed. Schedules automatically manage RolloutGate resources based on time windows, days of the week, and date ranges.
Schedule Types:
- RolloutSchedule: Namespaced resource that applies to rollouts in the same namespace
- ClusterRolloutSchedule: Cluster-scoped resource that can apply across multiple namespaces
Actions:
- Allow: Deployments are permitted during the schedule window, blocked outside it
- Deny: Deployments are blocked during the schedule window, permitted outside it
Rules: Multiple rules can be defined in a schedule. The schedule is active if ANY rule matches (OR logic).
Each rule can specify:
- timeRange: Specific hours in HH:MM format (24-hour)
- daysOfWeek: List of days (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday)
- dateRange: Specific date range in YYYY-MM-DD format
- timezone: IANA timezone format (e.g., "America/New_York", "Europe/London")
- The schedule controller evaluates time-based rules using the specified timezone
- For each rollout matching the
rolloutSelector, a RolloutGate is created/updated - The gate's
passingstatus is set based on schedule evaluation:- Action: "Allow" →
passing = active(allows rollouts when active) - Action: "Deny" →
passing = !active(blocks rollouts when active)
- Action: "Allow" →
- The rollout controller respects gates when deciding whether to deploy new versions
Allow deployments only during weekday business hours:
apiVersion: kuberik.com/v1alpha1
kind: RolloutSchedule
metadata:
name: business-hours-allow
namespace: default
spec:
rolloutSelector:
matchLabels:
schedule: business-hours
rules:
- name: "weekday-business-hours"
timeRange:
start: "09:00"
end: "17:00"
daysOfWeek:
- Monday
- Tuesday
- Wednesday
- Thursday
- Friday
timezone: "America/New_York"
action: Allow # Only allow rollouts during business hoursLabel your rollout to apply this schedule:
apiVersion: kuberik.com/v1alpha1
kind: Rollout
metadata:
name: my-app-rollout
labels:
schedule: business-hours
spec:
# ... rollout specPrevent deployments during peak traffic hours across production namespaces:
apiVersion: kuberik.com/v1alpha1
kind: ClusterRolloutSchedule
metadata:
name: production-peak-hours-deny
spec:
rolloutSelector:
matchLabels:
tier: frontend
namespaceSelector:
matchLabels:
environment: production
rules:
- name: "weekday-peak-hours"
timeRange:
start: "09:00"
end: "17:00"
daysOfWeek:
- Monday
- Tuesday
- Wednesday
- Thursday
- Friday
- name: "saturday-morning"
timeRange:
start: "09:00"
end: "12:00"
daysOfWeek:
- Saturday
timezone: "America/New_York"
action: Deny # Block rollouts during peak hoursAllow deployments only during scheduled maintenance windows:
apiVersion: kuberik.com/v1alpha1
kind: RolloutSchedule
metadata:
name: maintenance-window
namespace: production
spec:
rolloutSelector:
matchLabels:
maintenance: "true"
rules:
- name: "sunday-early-morning"
timeRange:
start: "02:00"
end: "06:00"
daysOfWeek:
- Sunday
timezone: "UTC"
action: AllowBlock all deployments during holiday periods:
apiVersion: kuberik.com/v1alpha1
kind: ClusterRolloutSchedule
metadata:
name: holiday-freeze
spec:
rolloutSelector:
matchLabels:
freeze: "holiday"
rules:
- name: "christmas-freeze"
dateRange:
start: "2026-12-23"
end: "2026-12-26"
- name: "new-year-freeze"
dateRange:
start: "2026-12-31"
end: "2027-01-02"
timezone: "America/New_York"
action: DenyRestrict certain rollouts to weekends only:
apiVersion: kuberik.com/v1alpha1
kind: RolloutSchedule
metadata:
name: weekend-only
namespace: default
spec:
rolloutSelector:
matchLabels:
schedule: weekend
rules:
- name: "weekend-deployment"
daysOfWeek:
- Saturday
- Sunday
timezone: "America/Los_Angeles"
action: AllowTime ranges automatically handle cross-midnight windows:
apiVersion: kuberik.com/v1alpha1
kind: RolloutSchedule
metadata:
name: night-deployment
namespace: default
spec:
rolloutSelector:
matchLabels:
schedule: night
rules:
- name: "overnight-window"
timeRange:
start: "22:00" # 10 PM
end: "06:00" # 6 AM (next day)
daysOfWeek:
- Monday
- Tuesday
- Wednesday
- Thursday
- Friday
timezone: "UTC"
action: AllowView the current status of schedules:
kubectl get rolloutschedules
kubectl get clusterrolloutschedulesOutput shows:
- ACTION: Allow or Deny
- ACTIVE: Current state (true/false)
- MATCHING: Number of rollouts matched by selectors
Describe a schedule for detailed information:
kubectl describe rolloutschedule business-hours-allowStatus includes:
active: Whether the schedule is currently activeactiveRules: Names of rules currently matchingnextTransition: When the active state will next changemanagedGates: List of RolloutGate names managed by this schedulematchingRollouts: Count of rollouts matched by selectors
A schedule is active if ANY rule matches. This enables flexible scheduling:
apiVersion: kuberik.com/v1alpha1
kind: RolloutSchedule
metadata:
name: flexible-window
namespace: default
spec:
rolloutSelector:
matchLabels:
schedule: flexible
rules:
- name: "morning-window"
timeRange:
start: "09:00"
end: "11:00"
- name: "afternoon-window"
timeRange:
start: "14:00"
end: "16:00"
- name: "weekend-anytime"
daysOfWeek:
- Saturday
- Sunday
timezone: "America/New_York"
action: AllowThis schedule allows deployments:
- 9-11 AM on any day
- 2-4 PM on any day
- Anytime on Saturday or Sunday
Schedules work by automatically creating and managing RolloutGate resources. Each managed gate is named: {schedule-name}-{rollout-name}.
You can view the gates created by schedules:
kubectl get rolloutgates -l "app.kubernetes.io/managed-by=rollout-schedule"The gates are automatically cleaned up when:
- The schedule is deleted
- A rollout no longer matches the selector
- The rollout is deleted
All schedules use IANA timezone database names. Common examples:
- UTC: "UTC"
- US Eastern: "America/New_York"
- US Pacific: "America/Los_Angeles"
- UK: "Europe/London"
- Central European: "Europe/Paris"
- Japan: "Asia/Tokyo"
If no timezone is specified, UTC is used by default.
Schedule not activating:
- Check the schedule status:
kubectl describe rolloutschedule <name> - Verify the timezone is correct
- Check that rollouts match the
rolloutSelectorlabels - Review the time range and day of week settings
- Check the
activeRulesin status to see which rules are matching
Deployments still blocked:
- Verify the action is correct (Allow vs Deny)
- Check if other RolloutGates are blocking the rollout
- Review rollout events:
kubectl describe rollout <name>
Cross-namespace schedules not working:
- Ensure you're using ClusterRolloutSchedule (not RolloutSchedule)
- Check the
namespaceSelectormatches target namespaces - Verify RBAC permissions for the controller
The Rollout Controller manages application deployments by:
- Monitoring available releases from Flux ImagePolicy
- Evaluating deployment gates
- Managing bake time for health checks
- Copying releases from source to target repositories
The rollout controller integrates with Flux's image automation components to provide a GitOps-friendly approach to release management. Instead of directly accessing OCI repositories, the controller uses Flux's ImagePolicy to determine available releases.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ ImageRepo │ │ ImagePolicy │ │ Rollout │
│ (Flux) │───▶│ (Flux) │───▶│ Controller │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ Target Repo │
│ (Deployment) │
└─────────────────┘
- Create an ImageRepository (Flux component):
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImageRepository
metadata:
name: my-app-repo
namespace: flux-system
spec:
image: ghcr.io/myorg/myapp
interval: 1m0s
secretRef:
name: ghcr-secret- Create an ImagePolicy (Flux component):
apiVersion: image.toolkit.fluxcd.io/v1beta2
kind: ImagePolicy
metadata:
name: my-app-policy
namespace: flux-system
spec:
imageRepositoryRef:
name: my-app-repo
policy:
semver:
range: '>=1.0.0'
prereleases:
enabled: true
allow:
- rc
- beta
- alpha
digestReflectionPolicy: IfNotPresent- Create a Rollout (this controller):
apiVersion: kuberik.com/v1alpha1
kind: Rollout
metadata:
name: my-app-rollout
spec:
releasesImagePolicy:
name: my-app-policy
versionHistoryLimit: 10
releaseUpdateInterval: "5m"
# Optional: bake time for health checks
bakeTime: "10m"
healthCheckSelector:
matchLabels:
app: myapp-
Release Discovery: The controller reads the
latestReffrom the ImagePolicy status to determine available releases. -
Version Selection: The controller applies semver logic to select the next release to deploy, considering:
- Current deployed version
- Available releases from ImagePolicy
- Gates and constraints
-
Resource Patching: The controller finds and patches Flux resources with specific annotations:
- OCIRepositories: Updates
spec.ref.tagfor resources withrollout.kuberik.com/rollout: <rollout-name> - Kustomizations: Updates
spec.postBuild.substitute.<NAME>for resources withrollout.kuberik.com/substitute.<NAME>.from: <rollout-name>
- OCIRepositories: Updates
-
Health Monitoring: During bake time, the controller monitors HealthChecks to ensure deployment stability.
The rollout controller uses annotations to identify which Flux resources should be managed:
apiVersion: source.toolkit.fluxcd.io/v1
kind: OCIRepository
metadata:
name: my-app-repo
annotations:
rollout.kuberik.com/rollout: "my-app-rollout" # References the rollout name
spec:
url: oci://ghcr.io/myorg/myapp
ref:
tag: "1.0.0" # This will be updated by the rollout controllerapiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: my-app-kustomization
annotations:
rollout.kuberik.com/substitute.IMAGE_TAG.from: "my-app-rollout" # References the rollout name
spec:
postBuild:
substitute:
IMAGE_TAG: "1.0.0" # This will be updated by the rollout controller- GitOps Integration: Leverages Flux's image automation for consistent, auditable release management
- Separation of Concerns: Image discovery is handled by Flux, deployment by the rollout controller
- Policy-Driven: Uses Flux's powerful image policy engine for release selection
- Observability: Integrates with Flux's monitoring and alerting capabilities
- Kubernetes cluster
- Flux v2 installed with image-reflector-controller
- kubectl configured
# Install CRDs
kubectl apply -f config/crd/bases/
# Install the controller
kubectl apply -k config/default/See the config/samples/ directory for complete examples including:
v1alpha1_imagerepository.yaml- Flux ImageRepository examplev1alpha1_imagepolicy.yaml- Flux ImagePolicy examplev1alpha1_rollout.yaml- Rollout controller examplev1alpha1_rolloutschedule.yaml- RolloutSchedule examplev1alpha1_clusterrolloutschedule.yaml- ClusterRolloutSchedule examplev1alpha1_rolloutschedule_*.yaml- Additional scheduling scenarios
make buildmake testmake run- Fork the repository
- Create a feature branch
- Make your changes
- Add tests
- Submit a pull request
Apache 2.0