Skip to content

Schedule Exceptions

This guide covers creating and managing temporary schedule overrides using ScheduleException resources.

Extending Hibernation for a Weekday Holiday

If your base schedule already covers weekday nights (e.g., start: "20:00", end: "06:00", daysOfWeek: ["MON", "TUE", "WED", "THU", "FRI"]), weekends are already fully hibernated — wakeup only triggers on listed days. So there's no need to extend for weekends.

A practical use of extend is covering a weekday holiday. Suppose Wednesday is a public holiday and there's no reason to wake up resources in the morning. The base schedule would wake resources at 06:00 on Wednesday, but you want them to stay hibernated all day. Add an extend exception to cover the daytime gap:

apiVersion: hibernator.ardikabs.com/v1alpha1
kind: ScheduleException
metadata:
  name: wednesday-holiday
  namespace: hibernator-system
spec:
  planRef:
    name: dev-plan
  type: extend
  validFrom: "2026-02-11T00:00:00Z"
  validUntil: "2026-02-11T23:59:59Z"
  windows:
    - start: "06:00"
      end: "20:00"
      daysOfWeek: ["WED"]
kubectl apply -f exception.yaml

This fills the 06:00–20:00 gap on Wednesday, creating continuous hibernation from Tuesday 20:00 through Wednesday 20:00 (when the normal nightly cycle begins again). The base schedule handles the rest — no need to modify it.

Suspending Hibernation for Cross-Timezone Work

When a team in a different timezone needs resources to stay alive beyond the normal hibernation start time, use a suspend exception to hold off hibernation.

Scenario: Your base schedule hibernates weekday resources in Asia/Jakarta from 20:00 to 06:00. A team in Japan (UTC+9, 2 hours ahead) needs to debug an issue on Wednesday evening and expects resources to stay up past the Jakarta 20:00 cutoff until end of day:

apiVersion: hibernator.ardikabs.com/v1alpha1
kind: ScheduleException
metadata:
  name: japan-team-debug
  namespace: hibernator-system
spec:
  planRef:
    name: dev-plan
  type: suspend
  validFrom: "2026-02-11T00:00:00Z"
  validUntil: "2026-02-11T23:59:59Z"
  leadTime: "1h"
  windows:
    - start: "20:00"
      end: "23:59"
      daysOfWeek: ["WED"]

The leadTime: "1h" prevents the controller from starting a new hibernate cycle within 1 hour before the suspension window begins (i.e., no hibernation from 19:00 onward on Wednesday). Resources stay awake until 23:59 on Wednesday instead of going down at 20:00.

Emergency Incident Override

To keep resources awake during an active incident that may span the full day:

apiVersion: hibernator.ardikabs.com/v1alpha1
kind: ScheduleException
metadata:
  name: incident-override
  namespace: hibernator-system
spec:
  planRef:
    name: prod-plan
  type: suspend
  validFrom: "2026-02-03T00:00:00Z"
  validUntil: "2026-02-04T23:59:59Z"
  windows:
    - start: "00:00"
      end: "23:59"
      daysOfWeek: ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"]

No leadTime — the suspension takes effect immediately.

Replacing the Schedule for Holidays

Replace the entire schedule during a holiday period:

apiVersion: hibernator.ardikabs.com/v1alpha1
kind: ScheduleException
metadata:
  name: holiday-week
  namespace: hibernator-system
spec:
  planRef:
    name: prod-plan
  type: replace
  validFrom: "2026-12-24T00:00:00Z"
  validUntil: "2026-12-31T23:59:59Z"
  windows:
    - start: "00:00"
      end: "23:59"
      daysOfWeek: ["MON", "TUE", "WED", "THU", "FRI", "SAT", "SUN"]

During the holiday week, the base schedule is ignored and replaced by the exception windows.

Monitoring Exceptions

Check Exception State

kubectl get scheduleexception -n hibernator-system
# NAME                  PLAN       TYPE      STATE    VALIDFROM                  VALIDUNTIL                 AGE
# wednesday-holiday     dev-plan   extend    Active   2026-02-11T00:00:00Z       2026-02-11T23:59:59Z       2d
# japan-team-debug      dev-plan   suspend   Pending  2026-02-11T00:00:00Z       2026-02-11T23:59:59Z       1h

Check Plan Exception History

kubectl get hibernateplan dev-plan -n hibernator-system \
  -o jsonpath='{.status.exceptionReferences}' | jq

Up to 10 exception references are tracked, ordered by active state first, then most recent.

Composing Exceptions

You can apply compatible exceptions to the same plan simultaneously. For example, an extend and suspend exception can coexist — the controller merges them using mergeByType semantics.

For detailed scenarios covering all type combinations (extend + suspend, replace + extend, replace + suspend) and validation rules, see Composing Multiple Exceptions.

Execution Overrides (Advanced)

extend and replace exceptions can also override the execution intent for a cycle: which targets run, their parameters, the execution strategy, and failure behavior. suspend exceptions support targetOverrides only (partial wakeup subset) — executionOverride remains forbidden on suspend. Anything not listed in an override follows the base plan spec as-is.

apiVersion: hibernator.ardikabs.com/v1alpha1
kind: ScheduleException
metadata:
  name: maintenance-override
  namespace: hibernator-system
spec:
  planRef:
    name: prod-plan
  type: extend
  validFrom: "2026-03-15T00:00:00Z"
  validUntil: "2026-03-16T23:59:59Z"
  windows:
    - start: "00:00"
      end: "23:59"
      daysOfWeek: ["SUN"]
  targetOverrides:
    - targetName: database
      disabled: true
    - targetName: compute
      parameters:
        instanceIds:
          - i-1234567890abcdef0
  executionOverride:
    strategy:
      type: Sequential
    behavior:
      mode: BestEffort

In this example:

  • The database target is skipped entirely for the cycle.
  • The compute target uses overridden parameters.
  • Targets run sequentially, and a single failure does not stop the cycle.

Weekend subset with suspend (e.g. wake 2 of 10 while hibernated, auto-revert on expiry):

apiVersion: hibernator.ardikabs.com/v1alpha1
kind: ScheduleException
metadata:
  name: weekend-dev-subset
  namespace: hibernator-system
spec:
  planRef:
    name: rds-fleet
  type: suspend
  validFrom: "2026-09-06T00:00:00Z"
  validUntil: "2026-09-07T23:59:00Z"
  windows:
    - start: "00:00"
      end: "23:59"
      daysOfWeek: ["SAT", "SUN"]
  targetOverrides:
    - targetName: db-prod-01
      disabled: true
    - targetName: db-prod-02
      disabled: true
    # unlisted targets follow base plan as-is; disabled targets stay stopped
    # (disable 8 of 10 to wake only the 2 dev instances for the weekend)

Only one exception with overrides per plan

extend, replace, and suspend share a single override slot: if one exception with targetOverrides/executionOverride is active for a plan, no second exception with overrides whose validity period overlaps it can be created — even an otherwise compatible extend + suspend pair. Plain exceptions without overrides can still coexist.

Disabling on DAG plans

Disabling any target — upstream or downstream — is allowed. A disabled target is treated as instantly succeeded, so dependents proceed normally.

Execution overrides are an advanced feature

Because execution overrides change the shape of a hibernation/wakeup cycle, they are best suited for operators who understand the target resources and failure modes. Misconfigured overrides can leave resources in an unexpected state.

Operational Semantics: Retry, Resume, and Restart

The controller locks the resolved execution intent when a cycle starts. This locked intent is preserved for the lifetime of the cycle, including transient errors and recovery:

Operation What happens to the exception intent
Automatic retry after a transient error Keeps the locked intent. The same targets, strategy, and behavior are retried.
Manual retry (retry-now annotation) Keeps the locked intent.
Resume from suspension mid-cycle Keeps the locked intent.
Restart (restart=true annotation) Keeps the locked intent by default. Add fresh=true to start a new cycle and re-evaluate from live state.
Manual override (override-action=true) Keeps the locked intent by default. Add fresh=true to start a new cycle and re-evaluate from live state.

This means you can safely delete or modify a ScheduleException while a cycle is running:

  • An in-flight cycle (including one in PhaseError waiting for retry) continues with the intent it started with.
  • A new cycle, triggered by restart/override-action with fresh=true, picks up the current plan and exception state.

If you need to abort the locked intent and start fresh, use restart=true together with fresh=true, or wait for the cycle to complete. For detailed command examples and the complete annotation reference, see the Override Actions guide.

Cleaning Up

Exceptions expire automatically based on validUntil. You can delete them early:

kubectl delete scheduleexception wednesday-holiday -n hibernator-system