# Cluster Configuration

> Cordium documentation. Canonical page: <https://octelium.com/docs/cordium/latest/management/clusterconfig>.

The `ClusterConfig` is the single source of truth for the *Cluster*-wide configuration of Cordium. It controls which *Users* can own *Spaces*, how *Workspace* and *Volume* storage is provisioned, the default and maximum resources and quotas, the inactivity timeouts, the *Cluster*-wide Linux capabilities and the Cordium Agent. There is exactly one `ClusterConfig` per *Cluster*. It is created automatically at installation time and it can only be read and updated by the *Cluster* administrators (read more [here](https://octelium.com/docs/cordium/latest/management/access-control.md#cluster-administrators)).

## Applying the Configuration

You can get the current `ClusterConfig` as follows:

```bash
cordium man get clusterconfig -o yaml
# Or simply
cordium man get cc -o yaml
```

And you can apply a new configuration from a file, a directory, or stdin as follows:

```bash
cordium man apply /PATH/TO/CLUSTERCONFIG.yaml
```

The file must contain a resource with `kind: ClusterConfig`. Here is a minimal example:

```yaml
kind: ClusterConfig
spec:
  space:
    ownership:
      rules:
        - effect: ALLOW
          condition:
            matchAny: true
```

> **Note:**
>
> Applying a `ClusterConfig` replaces its entire `spec`, which means that any section that is omitted from the applied file is reset to its default. Always start from your current configuration via `cordium man get cc -o yaml`, modify it and apply the result. Keeping the `ClusterConfig` in version control and applying it from your CI is the recommended way to manage it.

## Space Ownership

The `spec.space.ownership` field controls which *Users* are allowed to create (i.e. own) *Spaces*. It contains a list of rules, each with an `effect` (`ALLOW` or `DENY`) and a condition that is evaluated against the requesting *User* (i.e. `ctx.user`) and the *Space* being created (i.e. `ctx.space`). The `DENY` rules are evaluated first. If none of them matches, the `ALLOW` rules are evaluated, and if none of them matches either, the request is denied.

If `ownership` is not set, no *User* can create *Spaces*. Every *User* still gets their automatically created `default` *Space*. Here is an example that lets every *User* create personal `USER` *Spaces*, while only the members of the `platform` *Group* can create `ORGANIZATION` *Spaces*, and contractors cannot create *Spaces* at all:

```yaml
kind: ClusterConfig
spec:
  space:
    ownership:
      rules:
        - effect: DENY
          condition:
            match: '"contractors" in ctx.user.spec.groups'
        - effect: ALLOW
          condition:
            match: ctx.space.status.type == "USER"
        - effect: ALLOW
          condition:
            all:
              of:
                - match: ctx.space.status.type == "ORGANIZATION"
                - match: '"platform" in ctx.user.spec.groups'
```

The conditions have exactly the same syntax as the conditions of Octelium *Policies* (i.e. `match`, `matchAny`, `all`, `any`, `none`, `not` and `opa`) (read more [here](https://octelium.com/docs/octelium/latest/management/core/policy.md#condition)).

## Storage

The `spec.workspace.storage` field selects the Kubernetes `StorageClass` of the *Workspaces*' storage and the `VolumeSnapshotClass` of their snapshots via rules that are evaluated in order. The first matching rule wins. If no rule matches, the default `StorageClass` of the Kubernetes cluster and the default `VolumeSnapshotClass` of the CSI driver are used. Here is an example:

```yaml
kind: ClusterConfig
spec:
  workspace:
    storage:
      storageClass:
        rules:
          # Fast NVMe-backed storage for large Workspaces
          - condition:
              match: ctx.workspace.status.limit.storage.megabytes > 50000
            storageClass: longhorn-nvme
          - condition:
              matchAny: true
            storageClass: longhorn
      volumeSnapshotClass:
        rules:
          - condition:
              matchAny: true
            volumeSnapshotClass: longhorn-snapshot-vsc
```

The `storageClass` rules are evaluated against the *Workspace* (i.e. `ctx.workspace`), while the `volumeSnapshotClass` rules are evaluated against the *Workspace* (i.e. `ctx.workspace`) and, for *Template* pre-builds, its *Template* (i.e. `ctx.template`).

Similarly, the `spec.volume.storage.storageClass` rules select the `StorageClass` of *Volumes*, and they are evaluated against the *Volume* (i.e. `ctx.volume`). This is how you route `ACCESS_MODE_SHARED` *Volumes* to a multi-writer storage backend:

```yaml
kind: ClusterConfig
spec:
  volume:
    storage:
      storageClass:
        rules:
          - condition:
              match: ctx.volume.spec.accessMode == "ACCESS_MODE_SHARED"
            storageClass: nfs-csi
          - condition:
              matchAny: true
            storageClass: longhorn
```

You can read more about choosing and configuring storage backends [here](https://octelium.com/docs/cordium/latest/management/storage.md).

## Limits

The `spec.workspace.limit` field defines the *Cluster*-wide compute resources and quotas of *Workspaces*. All the fields are optional. Here is an example:

```yaml
kind: ClusterConfig
spec:
  workspace:
    limit:
      # The total number of Workspaces per User (defaults to 1000)
      maxPerUser: 100
      # The number of non-stopped Workspaces per User (defaults to 128)
      maxActivePerUser: 10
      # The total number of WorkspaceSnapshots per User (defaults to 100)
      maxSnapshotsPerUser: 50
      # The resources of the Template pre-builds
      buildLimit:
        cpu:
          millicores: 8000
        memory:
          megabytes: 16384
        storage:
          megabytes: 50000
      # The default resources of the Workspaces of USER Spaces
      defaultUserSpaceLimit:
        cpu:
          millicores: 2000
        memory:
          megabytes: 4096
        storage:
          megabytes: 20000
      # The default resources of the Workspaces of ORGANIZATION Spaces
      defaultOrganizationSpaceLimit:
        cpu:
          millicores: 4000
        memory:
          megabytes: 8192
        storage:
          megabytes: 30000
      # A hard cap that no Workspace of the Cluster can exceed
      maxLimit:
        cpu:
          millicores: 16000
        memory:
          megabytes: 65536
        storage:
          megabytes: 200000
```

The default limits only fill the fields that are not set by the *Workspace*, its *Template* or its *Space*, while the maximum limit caps the result (read more [here](https://octelium.com/docs/cordium/latest/workspaces/resources.md#resolution)). Without any configuration, *Workspaces* and pre-builds get 2 cores, 6000 MB of memory and 20000 MB of storage.

The `spec.volume.limit` field defines the limits of *Volumes*:

```yaml
kind: ClusterConfig
spec:
  volume:
    limit:
      # The number of Volumes per Space (defaults to 64)
      maxPerSpace: 32
      # The default size of new Volumes (defaults to 10 GB)
      defaultSize:
        megabytes: 20000
      # The maximum size of a Volume
      maxSize:
        megabytes: 500000
      # The number of Volume mounts per Workspace (at most 16)
      maxMountsPerWorkspace: 8
```

## Timeout

The `spec.workspace.timeout` field defines the inactivity timeouts after which running *Workspaces* are automatically stopped (read more [here](https://octelium.com/docs/cordium/latest/workspaces/resources.md#inactivity-timeout)). The timeout of a *Workspace* is the duration for its *Space* type if it is set, otherwise `defaultDuration`, otherwise 30 hours. Here is an example:

```yaml
kind: ClusterConfig
spec:
  workspace:
    timeout:
      defaultDuration:
        hours: 12
      userSpaceDuration:
        hours: 8
      organizationSpaceDuration:
        hours: 24
      # Lets Workspaces opt out via spec.runtime.timeout.mode: DISABLED
      allowNoTimeout: true
```

## Runtime

The `spec.workspace.runtime.capabilities` field adds or drops Linux capabilities for all the *Workspaces* of the *Cluster*. They are merged with the capabilities of the *Spaces* and the *Workspaces* (read more [here](https://octelium.com/docs/cordium/latest/workspaces/runtime.md#filesystem-and-capabilities)):

```yaml
kind: ClusterConfig
spec:
  workspace:
    runtime:
      capabilities:
        drop:
          - NET_RAW
          - SYS_PTRACE
```

## Agent

The `spec.agent` field configures the Cordium Agent for all the *Users* of the *Cluster*: whether it is enabled, its default Octelium LLM *Service* and model, its version, image, resources, and any additional agent configuration. Here is an example:

```yaml
kind: ClusterConfig
spec:
  agent:
    llm:
      service: claude
      model: claude-opus-5-5
    limit:
      cpu:
        millicores: 2000
      memory:
        megabytes: 4096
```

You can read the complete reference of the `agent` section [here](https://octelium.com/docs/cordium/latest/use/agent.md#configuring-the-agent).

## API Reference

[ClusterConfig JSON Schema (Cordium cordium/v1, latest documentation)](https://octelium.com/schemas/cordium/latest/cordium.v1.ClusterConfig.schema.json)
