Skip to content

About

Kubernetes controller for automatic resource cleanup and scheduled workload restarts πŸ•’ πŸ”„

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

lifecycle-controller

A Kubernetes controller that schedules resource deletion and Pod template updates through time-based annotations. It supports namespaced and cluster-scoped resources that expose the required API operations.

Use it to schedule resource deletion or request application restarts without a separate CronJob or wrapper object. Pod replacement depends on the workload's update behavior. See Restart mechanism.

Quick start

This example uses a Deployment named example-app in default namespace, without existing lifecycle action annotations. Actions are limited to Deployments in default namespace.

helm repo add lifecycle-controller https://cstanislawski.github.io/lifecycle-controller
helm repo update
helm install lifecycle-controller lifecycle-controller/lifecycle-controller \
  --namespace lifecycle-controller \
  --create-namespace \
  --set 'controllerManager.scope.watchResources[0]=deployments.apps' \
  --set 'controllerManager.scope.watchNamespaces[0]=default'
kubectl rollout status deployment/lifecycle-controller -n lifecycle-controller

Preview a restart after one minute:

kubectl annotate deployment example-app -n default \
  lifecycle.cezary.dev/dry-run="true" \
  lifecycle.cezary.dev/restart-after="1m" --overwrite
kubectl logs deployment/lifecycle-controller -n lifecycle-controller --since=5m
kubectl get events -n default \
  --field-selector involvedObject.kind=Deployment,involvedObject.name=example-app \
  --sort-by=.lastTimestamp

Check for the planned restart-after conversion in the logs and a DryRunRestart Event. Enable the action:

kubectl annotate deployment example-app -n default \
  lifecycle.cezary.dev/dry-run="false" --overwrite

The controller then converts restart-after to restart-at. After the scheduled restart time, check for a RestartTriggered Event and the Pod template's lifecycle.cezary.dev/restartedAt annotation. Use kubectl rollout status deployment/example-app -n default to check the workload after the template update.

Example use cases

Temporary development environments

To delete feature-branch resources after three days, apply a delete-after annotation to each resource.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: feature-branch-x-backend
  namespace: dev-features
  annotations:
    lifecycle.cezary.dev/delete-after: "3d" # Deletes this deployment after 3 days
spec:
  # ...

Nightly application restarts

To request a restart each day at 03:00 in a local timezone, use restart-cron and cron-timezone.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: legacy-app
  namespace: production
  annotations:
    lifecycle.cezary.dev/restart-cron: "0 3 * * *" # Daily at 3:00 AM
    lifecycle.cezary.dev/cron-timezone: "America/New_York"
spec:
  # ...

One-off scheduled maintenance

Use restart-at for a fixed restart time. For a restart after a database migration, use a workflow that waits for migration completion before applying the annotation.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: application
  namespace: production
  annotations:
    lifecycle.cezary.dev/restart-at: "2026-10-10T03:00:00Z"
spec:
  # ...

Annotation API reference

  • For all watched resources:

    • lifecycle.cezary.dev/reference-point (string) - specifies the starting point for relative duration timers (for -after annotations).
      • applyTimestamp (default) - the timer starts when the controller processes the -after annotation. Re-applying the manifest resets the timer ("keep-alive" behavior).
      • creationTimestamp - the timer starts from the resource's creation time. This creates a fixed TTL that is not affected by subsequent updates.
    • lifecycle.cezary.dev/delete-at - absolute TTL. The controller deletes the resource at or after this specific date and time (e.g., 2024-12-31T23:59:59Z).
      • The value must be an RFC3339 timestamp with explicit timezone offset (Z or Β±hh:mm).
      • This can be applied directly to a Namespace to trigger its deletion. Kubernetes will handle the subsequent removal of all resources within that namespace.
    • lifecycle.cezary.dev/delete-after - relative TTL (e.g., 5m, 1h, 3d). The controller calculates a deletion time from the selected reference point, saves it as lifecycle.cezary.dev/delete-at, and removes delete-after.
    • lifecycle.cezary.dev/catch-up - overrides the global policy for a missed deletion or restart. Accepts never, always, or a positive duration (e.g., 15m). See Missed actions.
    • lifecycle.cezary.dev/dry-run - enables dry-run for a resource. Accepts standard boolean values such as true, false, 1, and 0. Invalid values create a warning Event and no action is taken.
    • lifecycle.cezary.dev/managed-by: "lifecycle-controller" - added by the controller when it mutates a resource, such as converting *-after annotations to *-at annotations, maintaining restart schedule state, or triggering a restart.
  • For watched Deployments, StatefulSets, and DaemonSets in the apps API group:

    • lifecycle.cezary.dev/restart-at - requests one Pod template update at or after a specific date and time.
      • The value must be an RFC3339 timestamp with explicit timezone offset (Z or Β±hh:mm).
    • lifecycle.cezary.dev/restart-after - requests one Pod template update after a relative duration (e.g., 1h). The controller converts this to an absolute restart-at annotation.
    • lifecycle.cezary.dev/restart-every - requests recurring Pod template updates at a relative interval (e.g., 7d for weekly updates), with a minimum duration of 1m.
    • lifecycle.cezary.dev/restart-cron - requests recurring Pod template updates based on a standard five-field, minute-resolution cron expression (e.g., "0 3 * * *" for daily at 03:00).
    • lifecycle.cezary.dev/cron-timezone - optional timezone for restart-cron only. Must be valid IANA timezone (e.g., America/New_York). Defaults to UTC.
    • Other resources receive an UnsupportedRestartKind warning Event and their restart annotations remain unchanged.

Relative duration format

delete-after, restart-after, restart-every, and duration values for catch-up require exactly one positive integer and unit, without signs or spaces. Valid units are s, m, h, and d. Values such as 1d, 25h, and 90m are valid.

Zero, compound values (e.g., 1d2h), arithmetic expressions (e.g., 1d-25h), decimal values (e.g., 1.5h), and values that exceed the Go duration limit are invalid. Invalid values record an InvalidAnnotation warning Event and leaves the resource unchanged.

Controller behavior

Actions can run after their scheduled time. Restarts update the Pod template. The workload's update strategy determines whether Pods are replaced. See Controller behavior for more information.

RBAC

By default, the Helm chart watches standard workloads, ConfigMaps, Services, Ingresses, NetworkPolicies, PersistentVolumeClaims, HorizontalPodAutoscalers, PodDisruptionBudgets, Namespaces, and PersistentVolumes. It grants get, list, watch, patch, update, and delete for those resources, plus Event permissions and permissions for enabled leader-election and secure-metrics features.

A list of filtered namespaces creates a Role and RoleBinding in each watched namespace. Namespace patterns or leaving the list unset results in a ClusterRole. Namespace objects still require cluster permissions.

For resource patterns such as deploy*.apps, set rbac.create: false and supply your own RBAC with explicit resource names.

About

Kubernetes controller for automatic resource cleanup and scheduled workload restarts πŸ•’ πŸ”„

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages