> For the complete documentation index, see [llms.txt](https://docs.groundcover.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.groundcover.com/use-groundcover/custom-resource-workloads.md).

# Custom resource workloads

By default groundcover tracks built-in Kubernetes workload types such as Deployments, StatefulSets, and DaemonSets. Many clusters also run custom resources -- for example Argo Rollouts, OpenKruise CloneSets, or Spark Applications -- that own pods the same way a Deployment does.

groundcover can watch these Custom Resource Definitions (CRDs) so that their instances appear as first-class **workloads** in the Workloads page, Maps, filters, and monitors.

***

## How it works

1. **Discovery** -- On startup the groundcover sensor and the k8s-watcher backend list every CRD installed in the cluster.
2. **Filtering** -- Each CRD is matched against the `allowedKinds` list in the Helm values. Only CRDs whose **Kind** (case-insensitive) and **API group** match an entry in the list are watched.
3. **Watching** -- For every matched CRD, groundcover creates a watcher that tracks create, update, and delete events for all instances of that resource across the cluster.
4. **Workload classification** -- If the matching `allowedKinds` entry has `workload.enabled: true`, each instance of that CRD is classified as a **workload**. Pods owned by the CRD instance resolve to that workload name, and a Workload page is created in the UI.
5. **Name resolution** -- The workload name shown in groundcover is determined by an optional `nameFrom` config (see [Workload name resolution](#workload-name-resolution) below). When no custom resolution is configured, the Kubernetes object name is used.

***

## Default allowed kinds

groundcover ships with the following CRD kinds enabled by default:

| Kind             | API group                | Workload | Notes                                        |
| ---------------- | ------------------------ | -------- | -------------------------------------------- |
| Rollout          | `argoproj.io`            | Yes      | Argo Rollouts progressive delivery           |
| CloneSet         | `apps.kruise.io`         | Yes      | OpenKruise CloneSet workload controller      |
| Workflow         | `argoproj.io`            | Yes      | Argo Workflows (with custom name resolution) |
| SparkApplication | `sparkoperator.k8s.io`   | No       | Tracked as a CRD entity, not as a workload   |
| Runner           | `actions.summerwind.dev` | No       | GitHub Actions self-hosted runners           |

Kinds marked **Workload = Yes** appear on the Workloads page and in workload filters. Kinds marked **No** are tracked for entity metadata but do not create Workload pages.

{% hint style="info" %}
If the CRD is not installed in the cluster, the entry is silently ignored. You only need to remove an entry if you want to stop watching a CRD that is installed.
{% endhint %}

***

## Adding a custom CRD

To watch a new CRD kind and have its instances appear as workloads, add an entry under both the **k8s-watcher** and **sensor** sections of your Helm values override:

```yaml
# k8s-watcher configuration
k8sWatcher:
  watch:
    crd:
      enabled: true
      allowedKinds:
        # lowercase key — must match the CRD Kind (case-insensitive)
        mycustomkind:
          group: "example.com"          # CRD API group
          kindPlural: "mycustomkinds"   # plural resource name from the CRD spec
          workload:
            enabled: true               # makes instances appear as workloads

# sensor configuration
agent:
  sensor:
    k8sEntitiesWatch:
      crd:
        enabled: true
        allowedKinds:
          mycustomkind:
            group: "example.com"
            kindPlural: "mycustomkinds"
            workload:
              enabled: true
```

After applying the updated Helm values, the sensor and k8s-watcher pick up the new CRD on their next restart.

{% hint style="warning" %}
Both `k8sWatcher.watch.crd.allowedKinds` and `agent.sensor.k8sEntitiesWatch.crd.allowedKinds` must contain the same entry. The sensor watches CRD instances on each node for pod-to-workload resolution, while the k8s-watcher provides cluster-wide entity tracking.
{% endhint %}

### Required fields

| Field              | Description                                                                                                                                               |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `group`            | The API group of the CRD (e.g. `argoproj.io`). Must match exactly.                                                                                        |
| `kindPlural`       | The plural resource name from the CRD spec (e.g. `rollouts`, `clonesets`).                                                                                |
| `workload.enabled` | Set to `true` to classify instances as workloads. When `false` or omitted, instances are tracked as CRD entities but do not appear on the Workloads page. |

### Optional fields

| Field               | Description                                                                                                    |
| ------------------- | -------------------------------------------------------------------------------------------------------------- |
| `versions`          | A list of specific CRD versions to watch (e.g. `["v1alpha1"]`). When omitted, all served versions are watched. |
| `workload.nameFrom` | Custom workload name resolution. See below.                                                                    |

***

## Workload name resolution

When `workload.enabled: true`, groundcover determines the workload name for each CRD instance using the following priority:

1. **Labels** -- Check each label key listed in `nameFrom.labels` in order. The first non-empty label value becomes the workload name.
2. **Annotations** -- Check each annotation key listed in `nameFrom.annotations` in order. The first non-empty annotation value becomes the workload name.
3. **Generate name pattern** -- If the instance has a `metadata.generateName`, apply the regex in `nameFrom.generateNamePattern` and use the first capture group.
4. **Object name** -- Fall back to `metadata.name`.

If `nameFrom` is not configured, the object name is always used.

### Example: Argo Workflows

Argo Workflows dynamically generate workflow instance names (e.g. `daily-etl-xk92j`). The default configuration uses labels and generate-name patterns to group these instances under a stable workload name:

```yaml
workflow:
  group: "argoproj.io"
  kindPlural: "workflows"
  workload:
    enabled: true
    nameFrom:
      generateNamePattern: "^(.+)-$"
      labels:
        - "run-family"
        - "workflows.argoproj.io/workflow-template"
        - "workflows.argoproj.io/cron-workflow"
```

For a workflow instance with:

* `metadata.name`: `daily-etl-xk92j`
* `metadata.generateName`: `daily-etl-`
* `metadata.labels`: `{"workflows.argoproj.io/cron-workflow": "daily-etl"}`

Resolution proceeds as:

1. Check label `run-family` -- not present, skip.
2. Check label `workflows.argoproj.io/workflow-template` -- not present, skip.
3. Check label `workflows.argoproj.io/cron-workflow` -- found: `daily-etl`. **This becomes the workload name.**

If none of those labels existed, the generate-name pattern `^(.+)-$` would match `daily-etl-` and extract `daily-etl`.

If `generateName` was also empty, the raw object name `daily-etl-xk92j` would be used.

***

## Disabling CRD watching

To stop watching all CRDs, set `enabled: false`:

```yaml
k8sWatcher:
  watch:
    crd:
      enabled: false

agent:
  sensor:
    k8sEntitiesWatch:
      crd:
        enabled: false
```

To stop watching a specific kind, remove its entry from `allowedKinds` in both sections.

***

## RBAC

groundcover automatically generates ClusterRole rules for each entry in `allowedKinds`, granting `list`, `watch`, and `get` permissions on the CRD's API group and plural resource. No manual RBAC configuration is required.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.groundcover.com/use-groundcover/custom-resource-workloads.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
