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

# Manage worker queues with the APC API

> Read, create, resize, and delete a Deployment's worker queues with the APC API upsertDeployment mutation.

> [!NOTE]
>
> The examples on this page show common ways to use the APC API — they're not a complete API reference. For the full, interactive API documentation for your installation, including every available query, mutation, and type, go to `https://houston.<your-base-domain>/v1`, and click the **Docs** tab. See [Develop and test APC API queries](https://astronomer.io/docs/astro-private-cloud/v-2-x/houston-api-develop-test.md) for more on using the built-in GraphQL explorer.

Worker queues have no dedicated mutation. You manage them through the `workerQueues` argument on `upsertDeployment`. For what worker queues do and how to use them in the UI, see [Configure worker queues](https://astronomer.io/docs/astro-private-cloud/v-2-x/configure-worker-queues.md).

> [!NOTE]
>
> **Astro Private Cloud 2.2**
>
> This feature was introduced in Astro Private Cloud 2.2. To access this feature, upgrade your Astro Private Cloud installation to 2.2 or later.

> [!WARNING]
>
> The `workerQueues` argument is a full-list replace, not a partial patch. The list you send becomes the complete set of queues on the Deployment: a queue you leave out is deleted, and an optional field you leave out on a queue is reset to its default rather than keeping its stored value. Always send the full list with every field you want to keep.
>
> Omitting the `workerQueues` argument entirely leaves the queues untouched. An empty list is rejected.

The replace-all behavior defines the CRUD operations:

| Operation | What to send |
| - | - |
| **Read** | Query the Deployment's `workerQueues`. Each returned queue has a server-assigned `id`, `createdAt`, and `updatedAt`, but writes match queues by `name` — there's no `id` field on the input side. |
| **Add or update** | Send the full list with the new or changed entries included. Any field you omit on a resent entry resets to its default. |
| **Delete one** | Send the full list with that queue removed. The `default` queue can't be removed this way; see [Errors](#errors). |
| **Delete all** | Not possible. `workerQueues: []` is rejected, because a Deployment must always keep at least the `default` queue. |
| **Leave unchanged** | Omit `workerQueues` from the mutation entirely. |

## Enable worker queues for a Deployment

`deployments.workerQueues.enabled` gates worker queues and ships `false`. To turn it on for one Deployment, set a deployment-level config override:

```graphql wrap theme={null}
mutation enableWorkerQueues {
  updateDeploymentConfig(
    deploymentUuid: "<deployment-uuid>"
    deploymentsConfigOverride: {
      workerQueues: { enabled: true }
    }
    reason: "Enable worker queues"
  ) {
    config
    updatedAt
  }
}
```

> [!WARNING]
>
> `deploymentsConfigOverride` replaces the Deployment's entire stored override under `deployments.*`. A key you leave out stops overriding and inherits again, so read the current override first and resend everything you want to keep. See [Update Deployment configuration](https://astronomer.io/docs/astro-private-cloud/v-2-x/houston-update-deployment-config.md).

Add `maxPerDeployment` to the same object to raise or lower the queue cap for this Deployment:

```graphql wrap theme={null}
deploymentsConfigOverride: {
  workerQueues: { enabled: true, maxPerDeployment: 15 }
}
```

To set the flag more widely, use [`updateWorkspaceDeploymentsConfig`](https://astronomer.io/docs/astro-private-cloud/v-2-x/houston-update-workspace-config) for a Workspace, [`updateCluster`](https://astronomer.io/docs/astro-private-cloud/v-2-x/override-data-plane-cluster) for a cluster, or `values.yaml` for the whole platform. See [Enable worker queues](https://astronomer.io/docs/astro-private-cloud/v-2-x/configure-worker-queues.md#enable-worker-queues) and [Config governance](https://astronomer.io/docs/astro-private-cloud/v-2-x/config-governance.md).

## Read a Deployment's worker queues

```graphql wrap theme={null}
query deployment($releaseName: String) {
  deployment(where: { releaseName: $releaseName }) {
    id
    releaseName
    workerQueues {
      id
      name
      isDefault
      cpu
      memory
      ephemeralStorage
      replicas
      minWorkerCount
      maxWorkerCount
      workerConcurrency
      nodePoolId
    }
  }
}
```

`workerQueues` is an empty list on Kubernetes executor and Helm-mode Deployments, never `null`.

## Read the limits that apply to a Deployment

Resource bounds are overridable per cluster, Workspace, and Deployment, so read them for the Deployment you're changing instead of assuming the platform defaults:

```graphql wrap theme={null}
query deploymentConfig($deploymentUuid: Uuid) {
  deploymentConfig(deploymentUuid: $deploymentUuid) {
    workerQueues {
      cpu { min max }
      memory { min max }
      ephemeralStorage { min max }
      replicas { min max }
      workerConcurrencyMaxPerCore
      defaultEphemeralStorage
      maxPerDeployment
      canWorkerQueuesAutoscale
    }
  }
}
```

A range where either end is `null` isn't enforced.

`canWorkerQueuesAutoscale` is `true` when this Deployment's queues can run a worker range. See [Confirm autoscaling is available](https://astronomer.io/docs/astro-private-cloud/v-2-x/configure-worker-autoscaling.md#confirm-autoscaling-is-available).

## Add a worker queue

Send the complete list, including the existing `default` queue:

```graphql expandable wrap theme={null}
mutation upsertDeployment(
  $deploymentUuid: Uuid,
  $workerQueues: [InputWorkerQueue!]
) {
  upsertDeployment(
    deploymentUuid: $deploymentUuid,
    workerQueues: $workerQueues
  ) {
    id
    releaseName
    workerQueues {
      name
      isDefault
      cpu
      memory
      ephemeralStorage
      replicas
      workerConcurrency
      nodePoolId
    }
  }
}
```

With these variables:

```json expandable wrap theme={null}
{
  "deploymentUuid": "<deployment-uuid>",
  "workerQueues": [
    {
      "name": "default",
      "cpu": 1000,
      "memory": 3840,
      "ephemeralStorage": 51200,
      "replicas": 1,
      "workerConcurrency": 5
    },
    {
      "name": "heavy-etl",
      "cpu": 2000,
      "memory": 7680,
      "ephemeralStorage": 51200,
      "replicas": 3,
      "workerConcurrency": 10,
      "nodePoolId": "<node-pool-id>"
    }
  ]
}
```

## `InputWorkerQueue` fields

| Field | Type | Required | What omitting it does |
| - | - | - | - |
| `name` | `String!` | Yes | — |
| `cpu` | `Int!` | Yes | — |
| `memory` | `Int!` | Yes | — |
| `ephemeralStorage` | `Int` | No | Resets to `2048` |
| `replicas` | `Int` | No | Resets to `minWorkerCount` when you send both bounds, floored at `1`. Otherwise resets to `1` |
| `minWorkerCount` | `Int` | No | Falls back to `replicas`. The fewest workers the queue runs when it autoscales. See [Configure worker autoscaling](https://astronomer.io/docs/astro-private-cloud/v-2-x/configure-worker-autoscaling.md) |
| `maxWorkerCount` | `Int` | No | Falls back to `replicas`. The most workers the queue runs when it autoscales. See [Configure worker autoscaling](https://astronomer.io/docs/astro-private-cloud/v-2-x/configure-worker-autoscaling.md) |
| `workerConcurrency` | `Int` | No | Clears the override, so the operator default applies |
| `nodePoolId` | `String` | No | Clears the placement |

`cpu` is in millicores and `memory` and `ephemeralStorage` are in MiB. The API takes integers, not Kubernetes resource strings: send `2000`, not `"2"`, and `4096`, not `"4Gi"`.

Sending only one of `minWorkerCount` and `maxWorkerCount` leaves `replicas` at `1`, makes the missing bound fall back to it, and then rejects the inverted pair. Send both bounds or neither. To run a fixed number of workers, send `replicas` alone. To let a queue autoscale, send a `maxWorkerCount` above its `minWorkerCount`. See [Set a worker range on a queue](https://astronomer.io/docs/astro-private-cloud/v-2-x/configure-worker-autoscaling.md#set-a-worker-range-on-a-queue) for an example.

There's no `id` field and no `isDefault` field. Queues are matched by name across an update, and `isDefault` is derived from the name `default`.

## Resize a worker queue

Send the full list again with the changed values. This example doubles `heavy-etl` and leaves `default` as it was:

```json expandable wrap theme={null}
{
  "deploymentUuid": "<deployment-uuid>",
  "workerQueues": [
    {
      "name": "default",
      "cpu": 1000,
      "memory": 3840,
      "ephemeralStorage": 51200,
      "replicas": 1,
      "workerConcurrency": 5
    },
    {
      "name": "heavy-etl",
      "cpu": 2000,
      "memory": 7680,
      "ephemeralStorage": 51200,
      "replicas": 6,
      "workerConcurrency": 10,
      "nodePoolId": "<node-pool-id>"
    }
  ]
}
```

## Delete a worker queue

Send the list without it. Dropping `heavy-etl` from the previous example deletes that queue and its workers.

Nothing checks whether your Dag code still routes to the omitted queue. Tasks that name a deleted queue wait indefinitely with no error.

The `default` queue can't be dropped: a list that doesn't contain it is rejected.

## Update a Deployment without touching its queues

Omit the `workerQueues` argument. Any other `upsertDeployment` call — changing environment variables, alert emails, or the image — leaves the queues as they are:

```graphql wrap theme={null}
mutation upsertDeployment($deploymentUuid: Uuid, $label: String) {
  upsertDeployment(deploymentUuid: $deploymentUuid, label: $label) {
    id
    label
  }
}
```

## The legacy workers argument

The `workers` argument still works and aliases the `default` queue. Precedence is:

| Call | Result |
| - | - |
| Neither argument | Queues untouched |
| `workers` alone | Applied to the `default` queue, for the fields you set. Other queues untouched |
| `workerQueues` alone | Full-list replace, and `workers` is kept in sync from the `default` queue |
| Both | `workerQueues` wins for every field it sets |

A script written before worker queues existed keeps working against a multi-queue Deployment and doesn't collapse the other queues.

## Errors

| Error | Cause |
| - | - |
| `EmptyWorkerQueueListError` | You sent `[]`. To leave queues untouched, omit the argument |
| `DefaultWorkerQueueRequiredError` | The list has no queue named `default` |
| `DuplicateWorkerQueueNameError` | Two entries share a name |
| `TooManyWorkerQueuesError` | The list is longer than `deployments.workerQueues.maxPerDeployment` |
| `InvalidWorkerQueueNameError` | A name doesn't match `^[a-z]([-a-z0-9]*[a-z0-9])?$` |
| `WorkerQueueConcurrencyOutOfRangeError` | `workerConcurrency` is outside `[1, max]`, where `max` is 15 task slots per CPU core for that queue's `cpu` |
| `WorkerQueueExceedsNodePoolError` | A queue asks for more CPU, memory, or ephemeral storage than its node pool's ceiling |
| `NodePoolNotFoundError` | The `nodePoolId` doesn't exist, or belongs to another cluster |
| `WorkerQueueWorkerCountRangeError` | `maxWorkerCount` is below `minWorkerCount` |
| `WorkerQueueWorkerCountInvalidError` | `minWorkerCount` or `maxWorkerCount` is negative |
| `WorkerQueueWorkerCountExceedsLimitError` | `minWorkerCount` or `maxWorkerCount` is above the per-queue worker limit, `deployments.resourceManagement.components[workers].extra.replicas.limit` |
| `WorkerQueueReplicasOutsideRangeError` | `replicas` is outside `minWorkerCount` to `maxWorkerCount`. Leave `replicas` out to follow the minimum |
| `WorkerQueueZeroMinimumUnsupportedError` | `minWorkerCount` is `0` on a cluster that can't autoscale workers |
| `WorkerQueueRangeUnsupportedError` | `maxWorkerCount` is above `minWorkerCount` on a cluster that can't autoscale workers |
| `WorkerQueuesNotSupportedForModeError` | The Deployment isn't in operator mode |
| `WorkerQueuesNotSupportedForExecutorError` | The Deployment doesn't use the Celery executor |
| `WorkerQueueCreationDisabledError` | The feature flag is off and the list adds a new queue name. Existing queues can still be edited and removed |

Out-of-range `cpu`, `memory`, and `ephemeralStorage` values return `INTERNAL_SERVER_ERROR` rather than `BAD_USER_INPUT`. The message names the field, the value, and the allowed range.

## See also

* [Configure worker queues](https://astronomer.io/docs/astro-private-cloud/v-2-x/configure-worker-queues.md)
* [Configure worker autoscaling](https://astronomer.io/docs/astro-private-cloud/v-2-x/configure-worker-autoscaling.md)
* [Manage node pools with the APC API](https://astronomer.io/docs/astro-private-cloud/v-2-x/houston-manage-node-pools.md)
* [Upsert a Deployment](https://astronomer.io/docs/astro-private-cloud/v-2-x/houston-upsert-deployment.md)
