Reduce selected Deployments to zero replicas overnight and restore the captured replica count when the window ends. Overlapping schedules merge into one window, so workloads remain downscaled across adjoining periods.

Complete example

apiVersion: addons.projectcapsule.dev/v1alpha1
kind: ChangeWindow
metadata:
  name: development-downtime
spec:
  namespaceSelectors:
    - matchLabels:
        environment: development
    - matchLabels:
        environment: staging
  schedules:
    - name: weekday-nights
      cron: "0 19 * * 1-5"
      timeZone: Europe/Zurich
      duration: 12h
    - name: weekend
      cron: "0 7 * * 6"
      timeZone: Europe/Zurich
      duration: 48h
  serviceAccount:
    namespace: schedule-action-controller-system
    name: change-window-executor
  targets:
    - name: deployments
      apiVersion: apps/v1
      kind: Deployment
      selector:
        matchLabels:
          downscaling: enabled
  actions:
    - name: downscale
      targets: [deployments]
      capture:
        replicas:
          subresource: scale
          path: /spec/replicas
      onStart:
        apply:
          subresource: scale
          force: true
          template: |
            spec:
              replicas: 0
      onEnd:
        apply:
          subresource: scale
          template: |
            spec:
              replicas: {{ .captured.replicas }}
  admission:
    - name: freeze-deployments
      targets: [deployments]
      operations: [CREATE, UPDATE, DELETE]
      subresources: ["", scale]
      when: Active
      action: Deny
      message: >-
        Deployment changes are blocked during maintenance. Retry after {{ .window.end }}.
  config:
    admission:
      warnings:
        - name: upcoming-maintenance
          targets: [deployments]
          operations: [CREATE, UPDATE, DELETE]
          subresources: ["", scale]
          when: BeforeStart
          within: 1h
          message: >-
            Maintenance starts at {{ .window.start }} (in {{ .window.startsIn }}).
        - name: active-maintenance
          targets: [deployments]
          operations: [CREATE, UPDATE, DELETE]
          subresources: ["", scale]
          when: Active
          message: Maintenance is active until {{ .window.end }}.

Download addons_v1alpha1_changewindow.yaml

Executor permissions

Create the executor ServiceAccount and its narrowly scoped permissions first. Follow the executor setup instructions to adapt the sample to your Helm release. Repeat the workload Role and RoleBinding in every permitted namespace the window selects. The example’s serviceAccount must match the account granted these permissions.

# Example: an executor in the controller namespace may scale Deployments only in
# the existing namespace "development". Repeat workload RoleBindings as needed.
apiVersion: v1
kind: ServiceAccount
metadata:
  name: change-window-executor
  namespace: schedule-action-controller-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: impersonate-change-window-executor
  namespace: schedule-action-controller-system
rules:
  - apiGroups: [""]
    resources: [serviceaccounts]
    resourceNames: [change-window-executor]
    verbs: [impersonate]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: impersonate-change-window-executor
  namespace: schedule-action-controller-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: impersonate-change-window-executor
subjects:
  - kind: ServiceAccount
    name: schedule-action-controller-controller-manager
    namespace: schedule-action-controller-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: change-window-scale
  namespace: development
rules:
  - apiGroups: [apps]
    resources: [deployments]
    verbs: [get, list]
  - apiGroups: [apps]
    resources: [deployments/scale]
    verbs: [get, patch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: change-window-scale
  namespace: development
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: change-window-scale
subjects:
  - kind: ServiceAccount
    name: change-window-executor
    namespace: schedule-action-controller-system

Download executor-rbac.yaml

Start actions capture state before applying changes. Captures survive controller restarts. End actions restore the recorded resource UID, avoiding replacements that happen to reuse a deleted Deployment’s name. See resource actions for SSA ownership, subresources, restoration, and failure handling.