Profiles

4 min read

Profiles are named presets that expand into a fully-formed configuration at Katalog load time.

You declare a profile name. Orkestra resolves it into the concrete values — CPU limits, worker counts, probe timings, security settings — before the runtime starts. By the time a reconcile loop runs, there is no profile. There is only a fully-expanded spec, exactly as if you had written it by hand.


Why profiles exist

Most teams get Kubernetes resource configuration wrong — not because they don’t care, but because the right numbers require production data they don’t have yet. So they guess. They copy from Stack Overflow. They set limits too low and get OOMKilled at 2am, or too high and waste half the node. Probe timeouts are set once and never revisited. Security contexts are left empty because figuring out the right capabilities takes time no one has.

Profiles are a decision made once by someone who thought it through, shared with everyone who shouldn’t have to think about it again.

Writing raw Kubernetes configuration is correct but carries no intent:

resources:
  requests:
    cpu: 200m
    memory: 256Mi
  limits:
    cpu: 2
    memory: 2Gi

A profile captures the reason the numbers are what they are:

resources:
  profile: burst

burst tells you — and the next engineer who reads this Katalog — that this workload handles sudden spikes. The numbers follow from that decision, not the other way around.

The same logic applies to probe timing and security posture. Profiles encode intent. Intent survives change.


How profiles work

Every profile family follows the same pattern:

  1. Declare — write a profile name in the Katalog
  2. Validate — Orkestra checks the name at load time; unknown profiles fail fast
  3. Expand — Orkestra replaces the profile name with the full configuration
  4. Run — the runtime sees the expanded spec; the profile name is gone

Profiles can be static or dynamic:

# Static — always "steady"
resources:
  profile: steady

# Dynamic — resolved from the CR at reconcile time
resources:
  profile: "{{ .spec.resourceProfile | default \"small\" }}"

Static names are validated at load time. Template expressions are validated when the CR is reconciled.


Built-ins versus user-defined profiles

Orkestra ships with built-in profiles — deny-all, small/medium/large/xlarge, safe, zero-downtime — so the feature works immediately without any extra YAML. They cover common Kubernetes patterns.

But the feature is designed for the profiles you write yourself.

A team writing profile: org-medium is expressing an organizational contract: this namespace gets the capacity we agreed on for medium-sized teams. The numbers follow from that decision. Change the org-medium definition once and it propagates to every Katalog and Motif that references it — no grep, no PR chain, no drift.

That is what built-ins cannot do. medium is Orkestra’s guess at what medium means. org-medium is your team’s actual answer.

User-defined profiles are declared in a profiles: block at the root of any Katalog or Motif:

profiles:
  resourceQuotas:
    - name: org-medium
      description: Standard allocation for a medium-sized team namespace
      hard:
        pods: "25"
        cpu: "4"
        memory: "8Gi"

  networkPolicies:
    - name: org-deny-all
      description: Block all traffic — start here and add policies for what you need
      policyTypes: [Ingress, Egress]

  rollingUpdate:
    - name: org-safe
      description: Never reduces capacity during a rollout
      maxSurge: "1"
      maxUnavailable: "0"

They resolve before built-ins. A profile named deny-all in your profiles: block shadows the built-in. A conflict between two imported Motifs declaring the same profile name in the same class is a hard error at load time.

See User-defined profiles for the full reference.


Profile families

FamilyWhat it controlsApplied to
AutoscaleOperator worker scalingoperatorBox.autoscale
ResourceCPU and memory requests/limitsDeployment, StatefulSet, ReplicaSet, Pod, Job, CronJob
ProbeHealth check timingDeployment, StatefulSet, ReplicaSet, Pod
SecurityContainer and pod security contextsDeployment, StatefulSet, ReplicaSet, Pod, Job, CronJob
HPA BehaviorKubernetes HPA scale-up/down policieshpa[*].behavior
PDB BehaviorPodDisruptionBudget disruption limitspdb[*].behavior
Rolling UpdateDeployment/StatefulSet/ReplicaSet rollout strategydeployments[*].rollingUpdate, statefulSets[*].rollingUpdate, replicaSets[*].rollingUpdate
ResourceQuotaNamespace resource limits (pods, CPU, memory)resourceQuotas[*]
NetworkPolicyTraffic allow/deny rules for podsnetworkPolicies[*]
User-definedCustom named profiles declared in your Katalog or MotifAll profile-supporting fields

Rules that apply to every profile

  • Atomic — a profile fully defines its configuration. You cannot combine a profile with manual fields of the same type.
  • Fail-fast — an unknown profile name is a Katalog load error, not a runtime error.
  • No mixing — a profile and explicit fields of the same type cannot coexist on the same resource.
  • Template-safe — profile names can be template expressions. Static names are validated immediately; template expressions are validated at reconcile time.
  • User-defined — teams can declare custom named profiles in a Katalog or Motif profiles: block. They resolve before built-ins. See User-defined profiles.