Optimizing Workloads Owned by Custom Operators (CRDs)

In Kubernetes, many workloads are managed by “Operators” or custom controllers. For example, a Deployment might be owned and managed by an in-house custom resource. StormForge automatically detects these ownership relationships.

When a workload is owned by another resource (an “Operator”), StormForge typically targets the Owner for patching, rather than the workload itself. This ensures that your changes persist and aren’t overwritten by the operator’s reconciliation loop.

You can configure how StormForge interacts with these custom operators using the ownerResourceTypes Helm parameter.

Configuration

The ownerResourceTypes parameter allows you to:

  1. Define Patch Paths for custom operators (so StormForge knows how to update them).
  2. Ignore Owners, telling StormForge to patch the underlying workload instead.
  3. Stop Traversal, used to control owner identification in complex ownership chains.

Helm Parameter: ownerResourceTypes

The ownerResourceTypes parameter is a list of objects with the following fields:

Field Description Required
group The API group of the Operator (e.g., opentelemetry.io). Yes
kind The Kind of the Operator (e.g., OpenTelemetryCollector). Yes
ignoreOwner (Boolean) If true, StormForge ignores this operator and treats the child workload as independent. No
stopTraversal (Boolean) If true, StormForge stops looking for further owners beyond this one. No
patchTargetTypeDefaults (Map) Default patch paths. Required if you want to patch this operator type. Conditional

Patch Target Type Defaults

StormForge uses “Patch Paths” to know which fields in your Operator’s YAML to update when performing various optimization operations.

Common keys for patchTargetTypeDefaults:

  • live.stormforge.io/containers.cpu.requests.patch-path
  • live.stormforge.io/containers.cpu.limits.patch-path
  • live.stormforge.io/containers.memory.requests.patch-path
  • live.stormforge.io/containers.memory.limits.patch-path

Patch path values are Go templates that will be resolved to JSON pointers, with some extensions. For information on how to define Patch Path settings for your Operator, please contact StormForge support.

Scenario 1: Patching the Operator

If you use a custom operator like the OpenTelemetryCollector, you likely manage your configuration in the OpenTelemetryCollector CRD, not in the underlying Deployment. To optimize this, you might choose to tell StormForge how to write CPU and Memory settings back to that CRD.

Example: OpenTelemetry Collector

You want StormForge to optimize your collectors. The OpenTelemetryCollector resource owns the Deployment. You configure StormForge to target the Collector CRD.

ownerResourceTypes:
- group: opentelemetry.io
  kind: OpenTelemetryCollector
  resource: opentelemetrycollectors
  patchTargetTypeDefaults:
    # These paths define where resource settings live in the
    # OpenTelemetryCollector CRD
    live.stormforge.io/pod-template.metadata.patch-path: >
      /spec/podAnnotations
    live.stormforge.io/pod-template.metadata.patch-format: >
      { "stormforge.io/recommendation-url": "{{ .RecommendationURL }}" }
    live.stormforge.io/containers.cpu.requests.patch-path: >
      /spec/resources/requests/cpu
    live.stormforge.io/containers.cpu.limits.patch-path: >
      /spec/resources/limits/cpu
    live.stormforge.io/containers.memory.requests.patch-path: >
      /spec/resources/requests/memory
    live.stormforge.io/containers.memory.limits.patch-path: >
      /spec/resources/limits/memory

With this configuration, when StormForge optimizes the collector’s Deployment, it will patch the OpenTelemetryCollector operator owner, instead of the Deployment.

Scenario 2: Ignoring the Operator

Sometimes, an operator creates a workload but does not expose resource configuration in its own spec, or acts merely as a deployment trigger. In this case, patching the operator is impossible or useless.

You can use ignoreOwner: true to tell StormForge to skip this operator and patch the underlying workload (e.g., Deployment) directly.

Example: “Fire-and-Forget” Builder

Imagine an in-house builder, AppBuilder, that creates Deployments but stops managing them afterwards.

ownerResourceTypes:
- group: internal.example.com
  kind: AppBuilder
  ignoreOwner: true

Result: StormForge sees the Deployment owned by AppBuilder, but ignores that relationship. It treats the Deployment as the patch target.

Scenario 3: Stopping Traversal

In deep ownership chains (e.g., AppPlatform -> AppInstance -> Deployment), you might want to patch the AppInstance but not the top-level AppPlatform.

Setting stopTraversal: true on AppInstance tells StormForge: “Stop looking for owners here. Treat AppInstance as the owner.”

How It Works

  1. Workload Discovery: The StormForge Agent watches for workloads (like Deployments) and checks their owner references.
  2. Owner Traversal: If an owner is found, the agent traverses up the ownership chain until it finds the root owner or stops due to reaching a type for which stopTraversal is configured.
  3. Validate Owner Type: If the owner type is configured with ignoreOwner, StormForge will ignore the operator and treat the child workload as independent. Otherwise, the owner will be used as the workload’s patch target.
  4. Patching: When a recommendation is generated for the workload, some kinds of patches may target and be applied to the Owner resource.
Last modified January 14, 2026