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

# Create and Activate Deployment Groups

> Create deployment groups, target devices by tag and version, ship releases, and set the concurrency and failure limits that keep a bad update contained.

A deployment group connects firmware to the devices that should run it. It holds the targeting rules — which devices are in scope — and a sequence of **releases**, each shipping one firmware build. When a device checks in, NervesHub evaluates every active group against that device's tags and firmware version, and a match earns an update notification.

<Note>
  The web console calls these **deployment groups**, while the REST API and the `nh` CLI still use `deployment` / `deployments` in their paths and command names. They are the same object.
</Note>

<Steps>
  <Step title="Upload firmware">
    Before creating a group, you need a firmware UUID. If you have not yet uploaded your firmware, do so now:

    ```bash theme={null}
    nh firmware upload ./_build/rpi4_dev/nerves/images/my_project.fw
    ```

    The command prints the UUID of the uploaded firmware. Copy it — you will use it in the next step. If you are working with firmware that is already uploaded, retrieve its UUID with:

    ```bash theme={null}
    nh firmware list
    ```
  </Step>

  <Step title="Create a deployment group">
    Create a group that targets devices carrying a specific tag and running a qualifying firmware version:

    ```bash theme={null}
    nh deployment create \
      --name "v2.0-production" \
      --firmware <UUID> \
      --version "~> 1.0" \
      --tag "main"
    ```

    | Flag         | Description                                                                                                                                          |
    | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `--name`     | A human-readable name for the group. Must be unique within the product.                                                                              |
    | `--firmware` | The UUID of the firmware to deliver.                                                                                                                 |
    | `--version`  | A version requirement (using Elixir's `Version` syntax) that the device's **current** firmware must satisfy. Use `">= 0.0.0"` to match all versions. |
    | `--tag`      | The device tag that qualifies a device for this group. Pass it more than once to list several tags.                                                  |

    Newly created groups are **inactive** by default. No devices will receive the update until you explicitly activate the group in the next step.
  </Step>

  <Step title="Activate the group">
    Turn the group on to begin delivering firmware to matching devices:

    ```bash theme={null}
    nh deployment update "v2.0-production" state on
    ```

    Once active, NervesHub notifies eligible devices the next time they check in. Devices that are already online receive the notification within seconds.
  </Step>

  <Step title="Monitor the rollout">
    Track progress using the list and show commands:

    ```bash theme={null}
    nh deployment list
    nh deployment show "v2.0-production"
    ```

    `nh deployment show` reports how many devices have been notified, how many have successfully applied the update, and how many are pending or have failed.
  </Step>
</Steps>

## Targeting devices

A group targets on two conditions, and a device must satisfy both.

### Tags

Tags are arbitrary strings you assign to devices — `main`, `qa`, `beta`, `region-us-east`. A group lists one or more, plus a **tag operator** deciding how they combine:

| Tag operator              | Behaviour                                                |
| ------------------------- | -------------------------------------------------------- |
| `Require all` *(default)* | A device matches only if it carries **every** listed tag |
| `Allow any`               | A device matches if it carries **any** listed tag        |

So a group listing `beta` and `region-us-east` targets only devices carrying both under `Require all`, and every device carrying either under `Allow any`.

### Version condition

The group also filters on the firmware version a device is currently running — a semver expression such as `>= 1.0.0`, `~> 1.2`, or `< 2.0.0`. Only devices satisfying it are eligible. Leave it empty to match on tags alone.

Version conditions let you stage a migration: require devices to be on `~> 1.x` before they can receive `2.0`, so devices on unsupported older versions never pick up a breaking update.

<Note>
  A group also records the **platform** and **architecture** of the firmware it was created with, and every later release must match them. This stops firmware for one board being rolled out to a group full of another, so create a separate group per board rather than reusing one.
</Note>

## Releases

Firmware reaches a group through a release. Releases are numbered in the order they are created, each carries the firmware (and optional archive) being shipped plus an optional description and notes, and the group points at whichever is current.

Because the history is kept, you can see exactly which firmware a group shipped and when.

<Tip>
  A deployment group does not "finish". It stays active indefinitely, waiting for devices that match its conditions — including devices you register months later.
</Tip>

## Rollout safety controls

Each group carries limits that contain a bad release:

| Setting                       | Default | What it controls                                                        |
| ----------------------------- | ------- | ----------------------------------------------------------------------- |
| `concurrent_updates`          | `10`    | How many devices update at the same time                                |
| `device_failure_threshold`    | `3`     | Failures on a single device before it is put in the penalty box         |
| `device_failure_rate_amount`  | `5`     | Failures on a single device within the rate window before penalising it |
| `device_failure_rate_seconds` | `180`   | The rate window, in seconds                                             |
| `failure_threshold`           | `50`    | Fleet-wide failures before the group is flagged unhealthy               |
| `penalty_timeout_minutes`     | `1440`  | How long a penalised device waits before it may try again               |
| `queue_management`            | `FIFO`  | Whether the update queue is drained oldest-first or newest-first        |

A device that trips its failure thresholds goes into the **penalty box** and stops receiving update notifications until the timeout expires, which keeps one device stuck in a reboot loop from consuming rollout capacity. Clear it manually from the device page once you have addressed the cause.

<Note>
  Attaching a [workflow](/concepts/deployment-workflows) supersedes two of these: each step paces itself with its own `concurrent_updates`, and the step order replaces the priority queue.
</Note>

## Shipping a new release

To deliver a new firmware version through an existing group, update its firmware reference — this creates a new release:

```bash theme={null}
nh deployment update "v2.0-production" firmware <NEW_UUID>
```

Devices are evaluated against the new release on their next check-in. You can update the version condition or target tag the same way:

```bash theme={null}
nh deployment update "v2.0-production" tag "stable"
```

## Deactivating a group

Stop delivering updates to new devices without deleting the group:

```bash theme={null}
nh deployment update "v2.0-production" state off
```

<Tip>
  This is the fastest lever during an incident. Devices that have already applied the update are unaffected, but no further devices receive it until you reactivate.
</Tip>

Deactivating does not roll back devices that already applied the firmware.

## One-step upload and ship

For CI/CD pipelines where the latest firmware should go out immediately, use `--deploy` on the upload command:

```bash theme={null}
nh firmware upload my_project.fw --deploy "v2.0-production"
```

This uploads the firmware, creates a release on the named group, and activates it in one command. The group must already exist; `--deploy` does not create one.

## Staging a rollout

Everything above updates every matching device at the same pace. To roll out in stages instead — a small canary batch, a sign-off, then the rest — attach a workflow.

<Card title="Deployment Group Workflows" icon="diagram-project" href="/concepts/deployment-workflows">
  Define the stages of a rollout in a JSON file, with per-step targeting, concurrency, failure tolerance, and approval gates.
</Card>
