Scaling Signals
autoscale: conditions evaluate against the same Resolver data map used everywhere else in a Katalog. Any source resolved before deployment evaluation — external:, cross:, time notes, user-defined notes — is available as a field reference in when: and anyOf: blocks.
Time conditions
scaleUp:
conditions:
when:
- dayOfWeek:
weekday: true
- time:
after: "09:00"
before: "18:00"
target: 8
scaleDown:
conditions:
when:
- field: "{{ inBusinessHours }}"
equals: "false"
target: 2
Time conditions use the built-in time: and dayOfWeek: blocks, or user-defined notes that wrap them. The reconciler re-evaluates on every resync and converges to the correct replica count for the current time.
External HTTP
onReconcile:
external:
- name: queue
url: "{{ .spec.metricsEndpoint }}"
timeout: 5s
continueOnError: true
deployments:
- name: "{{ .metadata.name }}"
autoscale:
scaleUp:
conditions:
when:
- field: external.queue.pendingJobs
greaterThan: "100"
increment: 2
external: resolves before deployment evaluation. The response is available at external.<name>.* when conditions are checked. Any JSON HTTP endpoint works: Kafka consumer lag, RabbitMQ queue depth, SQS queue attributes, Prometheus instant queries, custom APIs. When the response body is a valid JSON object, its keys are merged directly into the result — external.queue.pendingJobs works for { "pendingJobs": 100 }, and external.queue.metrics.pendingJobs works for { "metrics": { "pendingJobs": 100 } }.
continueOnError: true means a temporarily unavailable endpoint does not error the reconcile — conditions are not met, the Deployment holds its current replica count, and the operator retries on the next resync.
Cross-operator metrics
cross:
- crd: jobqueue
selector:
name: "{{ .spec.queueName }}"
namespace: "{{ .metadata.namespace }}"
as: queue
deployments:
- name: "{{ .metadata.name }}"
autoscale:
scaleUp:
conditions:
when:
- field: cross.queue.status.queueDepth
greaterThan: "100"
increment: 2
cross: reads a sibling CRD’s resolved state. In-binary lookups go through the informer cache. For cross-binary or cross-cluster, cross: with a source: block fetches via HTTP with caching. Any field the sibling publishes to status is reachable at cross.<name>.status.*.
Notes as scaling guards
scaleUp:
conditions:
when:
- field: "{{ hasCrashingPod .children.deployment }}"
equals: "false"
- field: external.queue.pendingJobs
greaterThan: "100"
increment: 2
Notes can guard scale-up — here, the operator only scales when there are no crashing pods and the queue is deep. Notes that return booleans evaluate naturally in when: blocks.
Composite conditions
scaleUp:
conditions:
anyOf:
- field: external.kafka.consumerLag
greaterThan: "1000"
- field: cross.worker.metrics.queueDepth
greaterThan: "200"
- time:
after: "08:00"
before: "09:00"
dayOfWeek:
weekday: true
anyOf: evaluates as OR — scale up if any condition passes. when: evaluates as AND — all conditions must pass. Both can appear in the same conditions: block; when: and anyOf: are evaluated independently and combined with AND.