> ## Documentation Index
> Fetch the complete documentation index at: https://docs.controlplane.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Types

> Comparison of workload types: Standard (default), Stateful (persistent storage), Cron (scheduled jobs), and Serverless (scale-to-zero). Capabilities matrix.

|                                                                  | Standard | Stateful | Cron | Serverless |
| :--------------------------------------------------------------- | :------: | :------: | :--: | :--------: |
| Allow multiple containers                                        |     ✔    |     ✔    |   ✔  |      ✔     |
| Scale to Zero                                                    |   KEDA   |   KEDA   |      |      ✔     |
| Must expose one HTTP port                                        |          |          |      |      ✔     |
| Allow no exposed ports                                           |     ✔    |     ✔    |   ✔  |            |
| Allow multiple exposed ports                                     |     ✔    |     ✔    |      |            |
| Unable to expose any ports                                       |          |          |   ✔  |            |
| Custom Domain requests have the HOST header of the custom domain |     ✔    |     ✔    |      |            |
| Fast switching update between versions                           |          |          |      |      ✔     |
| Rolling update between versions                                  |     ✔    |     ✔    |      |            |
| [Capacity AI](/reference/workload/capacity)                      |     ✔    |          |      |      ✔     |
| Autoscale by CPU                                                 |     ✔    |     ✔    |      |      ✔     |
| Autoscale by requests per second                                 |     ✔    |     ✔    |      |      ✔     |
| Autoscale by concurrent requests                                 |          |          |      |      ✔     |
| Autoscale by request latency                                     |     ✔    |     ✔    |      |            |
| Runs on a schedule and is expected to complete                   |          |          |   ✔  |            |
| Readiness/liveness probes default to TCP check of container port |          |          |      |      ✔     |
| Readiness/liveness probes default to disabled                    |     ✔    |     ✔    |   ✔  |            |

## Standard

Standard workloads have greater flexibility in network exposure. They can scale to zero only via KEDA.

Standard workloads **may**:

* Expose no network endpoint.
* Serve traffic on multiple ports.
* Scale to zero when using KEDA as the autoscaling metric.

Standard workloads **may not**:

* Scale to zero with `cpu`, `memory`, `rps`, `latency`, or multi-metric strategies.

## Stateful

Stateful workloads provide stable replica identities and persistent storage, making them ideal for databases, message queues, and other applications that require durable state. Each replica maintains a consistent identity across restarts and can be addressed individually.

Stateful workloads **may**:

* Expose no network endpoint.
* Serve traffic on multiple ports.
* Mount [volume sets](/reference/volumeset) for persistent storage.
* Scale to zero when using KEDA as the autoscaling metric.

Stateful workloads **may not**:

* Scale to zero with `cpu`, `memory`, `rps`, `latency`, or multi-metric strategies.
* Use [Capacity AI](/reference/workload/capacity).

### Overview

Stateful workloads are similar to [standard](#standard) and [serverless](#serverless) workloads in many ways. Much of the base functionality, including Universal Cloud Identity, metrics, logs, audit trail, etc. remains the same.

### Replica-Direct Endpoints

By default, stateful workloads (like other types) receive load-balanced endpoints. To enable one endpoint per replica, set `spec.loadBalancer.replicaDirect` to `true`. Enable this when clients need to target a specific replica directly—for example, connecting to a particular database node or routing requests to a designated leader in a clustered application. See the [Replica-Direct Endpoints documentation](/reference/workload/general#replica-direct-endpoints) for the endpoint format.

### Capacity AI

Stateful workloads **do not** support [Capacity AI](/reference/workload/capacity). To optimize costs specify `minCpu` and `minMemory`

### Autoscaling

Stateful workloads **do not** support concurrency-based horizontal autoscaling.

### Cost Optimization

To reduce costs when using a stateful workload, set `minCpu` and `minMemory` to a low value. These values are used to reserve resources on Control Plane. In Kubernetes parlance, these values are the resource requests. Control Plane bills based on reserved resources, so lower minimum values directly reduce your costs when actual usage is below the maximum.

#### `minCpu`

* `minCpu` and `cpu` can be at most 4000m apart
* The ratio between `minCpu` and `cpu` must be at least 1:4

#### `minMemory`

* `minMemory` and `memory` can be at most 4096Mi apart
* The ratio between `minMemory` and `memory` must be at least 1:4

### Key Features

#### Stable Replica Identities

Each replica has a permanent identity. If a replica is rescheduled, restarted, or recreated for any reason, its identity will remain constant. Identities are of the form {`workloadName`}-{`replicaIndex`} e.g. for a workload called my-workload, the replica identities would be:

* my-workload-0
* my-workload-1
* etc.

#### Stable Local Hostnames

Each replica has a hostname corresponding to its identity. Hostnames are of the form `{replicaIdentity}.{workloadName}` e.g. for a workload called my-workload, the replica hostnames would be:

* my-workload-0.my-workload
* my-workload-1.my-workload
* etc.

These hostnames are only accessible within the same location. They cannot be exposed publicly or accessed from other locations. To enable cross-location and public access to individual replicas, set `spec.loadBalancer.replicaDirect` to `true` (see [Replica-Direct Endpoints](#replica-direct-endpoints)).

To make an HTTP request to a specific replica within the same location:

`curl http://my-workload-1.my-workload:8080` from another workload running on Control Plane

#### Persistent Storage

Stateful workloads can mount a [volume set](/reference/volumeset) as a volume in one or more of its containers.
To do this, simply add a volume to a container's list of volumes of the form:

```yaml YAML theme={null}
uri: cpln://volumeset/my-volume-set
path: /some/mount/directory
```

#### Considerations When Using Persistent Storage

* Volume sets are [GVC](/reference/gvc) scoped.
* A workload can only use volume sets in the same GVC.
* A volume set can be used by at most one workload, unless you use the [shared file system type](/reference/volumeset#shared-file-system), which provisions a single volume per location that multiple workloads can access simultaneously.
* A workload may use any number of volume sets.
* The volume set uri must begin with `cpln://volumeset/`.

### Examples

#### PostgreSQL

[This example](https://github.com/controlplane-com/examples/tree/main/examples/postgres) demonstrates how to run a simple postgresql instance on Control Plane.

#### NATS

[This example](https://github.com/controlplane-com/examples/tree/main/examples/nats) demonstrates how to run [NATS](https://nats.io/) on Control Plane.

### Minimal Example

```yaml YAML theme={null}
kind: workload
name: my-database
spec:
  type: stateful
  containers:
    - name: db
      image: postgres:16
      cpu: "1"
      minCpu: "250m"
      memory: 1Gi
      minMemory: 256Mi
      ports:
        - number: 5432
          protocol: tcp
      volumes:
        - uri: cpln://volumeset/my-data
          path: /var/lib/postgresql/data
  defaultOptions:
    autoscaling:
      minScale: 1
      maxScale: 3
  loadBalancer:
    replicaDirect: true
```

## Cron

Cron workloads should be used when you need to perform a background task on a regular schedule.

Cron workloads **may not**:

* Serve traffic.
* Scale to zero.
* Include a container that runs indefinitely.

Cron workloads **must**:

* Exit upon completion of the task at hand. Control Plane will start a new replica of your workload at the next scheduled execution time.

<Note>
  A cron workload can be suspended by setting `job.suspend` to `true`. While suspended, no new executions start. Use [Run Now](#run-now) for on-demand execution even while suspended.
</Note>

### Cron Configuration

<Info>
  Cron workloads are always deployed to all locations within their GVC. Unlike workloads of other types, there is no way to provide location-specific configuration overrides.
</Info>

* `job.schedule`
  * A string, which determines how often the cron workload should execute. This field uses the [Kubernetes cron schedule syntax](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/#cron-schedule-syntax).
* `job.concurrencyPolicy`
  * This determines what Control Plane will do when a prior execution of your workload is still running when the next scheduled execution time arrives.
    * `Forbid`: subsequent executions will be forgone until the running execution completes.
    * `Replace`: the running execution will be stopped so that a new execution can begin.
    * `Allow`: subsequent executions will be allowed to proceed as normal.
* `job.historyLimit`
  * An integer between 1 and 10 representing the number of prior executions to be retained for reference.
* `job.restartPolicy`
  * Either `Never` or `OnFailure`. This determines whether your workload will be restarted when it fails on execution.
* `job.activeDeadlineSeconds`
  * **Optional**: By default there is no deadline. Job executions are allowed to run indefinitely.
  * If this property is set, this is the maximum number of a seconds a job execution can run. If the job does not exit in the allotted time, Control Plane will remove it.

### Run Now

<Tip>Cron workloads can be run on-demand even when they are suspended.</Tip>

<Info>When running a cron workload on-demand the job.concurrencyPolicy field is ignored.</Info>

Cron workloads can be run on-demand by submitting a `runCronWorkload` command via `POST https://api.cpln.io/org/my-org/gvc/my-gvc/workload/my-cron-workload/-command`

```yaml runCronWorkload Command theme={null}
type: runCronWorkload
spec:
  location: aws-us-west-2
  containerOverrides:
    - name: my-container
      command: '/bin/sh'
      cpu: '25m'
      memory: '32Mi'
      image: 'ubuntu:latest'
      args:
        - '-c'
        - sleep 10
      env:
        - name: MY_ENV_VAR
          value: some-new-value
```

#### runCronWorkload Spec

* `location`
  * The name of the location as specified in your [GVC](/reference/gvc) configuration.
* `containerOverrides`
  * **Optional**: A list of objects that override specific parts of a container's configuration.
* `containerOverrides[].name`
  * The name of the container to override in the workload specification.
* `containerOverrides[].command`
  * A new command for the container during this execution only. This field corresponds to `workload.containers[].command` in the workload specification.
* `containerOverrides[].cpu`
  * A new CPU configuration for the container during this execution. This field corresponds to `workload.containers[].cpu` in the workload specification.
* `containerOverrides[].memory`
  * A new memory allocation for the container during this execution. This field corresponds to `workload.containers[].memory` in the workload specification.
* `containerOverrides[].image`
  * A new image for the container during this execution. This field corresponds to `workload.containers[].image` in the workload specification.
* `containerOverrides[].args`
  * A new list of arguments for the container during this execution only. This field corresponds to `workload.containers[].args` in the workload specification.
* `containerOverrides[].env`
  * A new list of environment variables for the container during this execution only. This field corresponds to `workload.containers[].env` in the workload specification.

#### Run via the CLI

Run a cron workload using the `cpln` CLI via `cpln workload cron start`. e.g.

<Tip>If you do not specify a location using --location, the CLI will run the workload everywhere it has been deployed.</Tip>

<Info>The --file parameter should contain a list of containerOverrides.</Info>

```shell CLI Example theme={null}
echo '[{"name":"my-container","command":"/bin/bash","args":["-c","sleep 10"],"env":[{"name":"MY_ENV_VAR","value":"some-new-value"}]}]' | cpln workload cron start cron --gvc my-gvc --org my-org --file -
```

### Job History

A cron workload retains up to `job.historyLimit` job executions in its history. Each job execution will be in one of the following statuses:

* Invalid
* Active
* Success
* Failure
* Removed

#### The Removed Status

The Removed status indicates that a job execution was deleted before it could finish execution. There are several reasons this can happen, but the most common are:

* The `job.concurrencyPolicy` is `Replace` and while the job was still executing, `job.Schedule` dictated that the job should begin again.
* The `job.activeDeadlineSeconds` limit was exceeded.

## Serverless

Serverless workloads should be used for web applications that serve traffic on a single port, but may not need to run 100% of the time.

Serverless workloads **may**:

* Scale to zero.

Serverless workloads **may not**:

* Serve traffic on multiple ports.

Serverless workloads **must**:

* Expose a network endpoint.

<Note>
  When a custom domain routes to a serverless workload, the `Host` header is set to the canonical endpoint, not the custom domain. The original domain is available in the `X-Forwarded-Host` header. See [Hostname Behavior](/reference/domain#hostname-behavior) for details.
</Note>
