Managing device fleets

Red Hat Edge Manager 1.3

Managing device fleets with Red Hat Edge Manager

Red Hat Edge Manager Documentation Team

Abstract

This document provides information about Managing device fleets with Red Hat Edge Manager

Chapter 1. Managing device fleets

The Red Hat Edge Manager simplifies the management of a large number of devices and workloads through device fleets. A fleet is a resource that defines a group of devices governed by a common device template and management policies.

When you make a change to the device template, all devices in the fleet receive the changes when the Red Hat Edge Manager agent detects the new target specification.

Device monitoring in a fleet is also simplified because you can check the status summary of the whole fleet.

When vulnerability reporting is enabled, you can review CVE data aggregated for a fleet and drill into per-fleet impact from the Security overview page. For more information, see View fleet vulnerabilities.

Fleet-level management offers the following advantages:

  • Scales your operations because you perform operations only once for each fleet instead of once for each device.
  • Minimizes the risk of configuration mistakes and configuration drift.
  • Automatically applies the target configuration when you add devices to the fleet or replace devices in the fleet.

The fleet specification consists of the following features:

Label selector
Determines which devices are part of the fleet.
Device template
Defines the configuration that the Red Hat Edge Manager enforces on devices in the fleet.
Policies
Govern how devices are managed, for example, how changes to the device template are rolled out to the devices.

A fleet resource does not run software itself; it carries a device template whose specification (including choices you make from the Software Catalog) is enforced on each device that matches the fleet label selector.

You can deploy or update catalog software on an individual device, or apply catalog items to a fleet so that the device template is updated and matching devices receive the software. Updates are available from the Catalog tab when you open a device or a fleet in the Flight Control web console.

Catalog updates use the same published upgrade paths for each catalog item, whether you target a single device or all devices governed by a fleet. See Validated upgrade paths and compatibility in the Additional resources section.

You can have both individually managed and fleet-managed devices at the same time. When a device is selected into a fleet, the Red Hat Edge Manager creates the device specification for the new device based on the device template. If you update the device template for a fleet or a new device joins the fleet, the Red Hat Edge Manager enforces the new specification in the fleet.

If a device is not selected into any fleets, the device is considered user-managed or unmanaged. For user-managed devices, you must update the device specification either manually or through an external automation.

Important

A device cannot be a member of more than one fleet at the same time.

For more information, see Labels and label selectors.

Device selection into a fleet

By default, devices are not assigned to a fleet. Instead, each fleet uses a selector that defines which labels a device must have to be added to the fleet.

Note

Fleet selectors always match devices based on labels. Values that the agent reports in status.systemInfo, such as hardware, operating system, or site attributes, are not directly selectable in fleet selectors.

Map those values to labels during enrollment by using label-from-systeminfo in the agent configuration. You can then use the resulting labels in fleet selectors and template placeholders. For more information, see Enrollment-time label mapping in the Additional resources section.

To understand how to use labels in a fleet, see the following example:

The following list shows point-of-sales terminal devices and their labels:

Table 1.1. Device label examples

DeviceLabels

A

type: pos-terminal, region: east, stage: production

B

type: pos-terminal, region: east, stage: development

C

type: pos-terminal, region: west, stage: production

D

type: pos-terminal, region: west, stage: development

If all point-of-sales terminals use the same configuration and are managed by the same operations team, you can define a single fleet called pos-terminals with the type=pos-terminal label selector. Then, the fleet contains devices A, B, C, and D.

However, you might want to create separate fleets for the different organizations for development or production. You can define a fleet for development with the type=pos-terminal, stage=development label selector, which selects devices B and D. Then, you can define another fleet for production with the type=pos-terminal, stage=production label selector. By using the correct label selectors, you can manage both fleets independently.

Important

You must define selectors in a way that two fleets do not select the same device.

For example, if one fleet selects region=east, and another fleet selects stage=production, both fleets try to select device A. If two fleets try to select the same device, the Red Hat Edge Manager keeps the device in the currently assigned fleet, if any, and sets the MultipleOwners condition on the affected devices to true.

Fleets with mixed OS modes

Fleets can contain devices running in different OS modes (image and package). When a fleet template includes an OS image target, image-mode devices process the full spec normally, while package-mode devices reject the entire spec atomically. No partial configuration or application delivery occurs on package-mode devices that reject a spec. Package-mode devices appear as OutOfDate in fleet status when the fleet template includes an OS image they cannot apply.

For details, see Mixed-fleet behavior with image-mode and package-mode devices and Resolving mixed-fleet rollout issues in the Additional resources section.

Device templates

A device template of a fleet contains a device specification that is applied to all devices in the fleet when the template is updated.

For example, you can specify in the device template of a fleet that all devices in the fleet must run the registry.example.com/edge/rhel:9.5 operating system image.

The Red Hat Edge Manager service then rolls out the target specification to all devices in the fleet and the Red Hat Edge Manager agents update each device accordingly.

When the fleet template references Git, HTTP, or Kubernetes secret configuration providers, Red Hat Edge Manager can also create new template versions and roll out changes when those upstream sources change, without you editing the fleet specification. For details, see Automatic synchronization of external configuration in the Additional resources section.

You can change other specification items in the device template and the Red Hat Edge Manager applies the changes in the same way.

However, sometimes not all of the devices in the fleet need to have the exact same specification. Templates can contain placeholders that are populated based on the device name or label values.

The syntax of the placeholders matches that of Go templates. You can use simple text, actions, and conditional expressions such as if, else, else if, and with blocks, along with the comparison functions eq, ne, lt, gt, le, ge, and, or, and not.

The use of loops (range) in the placeholders is not supported.

You can reference anything from the metadata of a device, such as {{ .metadata.labels.key }} or {{ .metadata.name }}.

You can also use the following functions in your placeholders:

  • The upper function changes the value to uppercase. For example, the function is {{ upper .metadata.name }}.
  • The lower function changes the value to lowercase. For example, the function is {{ lower .metadata.labels.key }}.
  • The replace function replaces all occurrences of a substring with another string. For example, the function is {{ replace "old" "new" .metadata.labels.key }}.
  • The getOrDefault function returns a default value if accessing a missing label. For example, the function is {{ getOrDefault .metadata.labels "key" "default" }}.

You can combine the functions in pipelines, for example, a combined function is {{ getOrDefault .metadata.labels "key" "default" | upper | replace " " "-" }}.

Note

Ensure that you use proper Go template syntax. For example, {{ .metadata.labels.target-revision }} is not valid because of the hyphen. Instead, you must refer to the field as {{ index .metadata.labels "target-revision" }}.

You can use the placeholders in device templates in the following ways:

  • You can label devices by deployment stage, for example, stage labels are stage: testing and stage: production. Then, you can use the label with the stage key as placeholder when referencing the operating system image to use, for example, use quay.io/myorg/myimage:latest-{{ .metadata.labels.stage }} or when referencing a configuration folder in a Git repository.
  • You can label devices by deployment site, for example, deployment sites are site: factory-berlin and site: factory-madrid.
  • Then, you can use the label with the site key as parameter when referencing the secret with network access credentials in Kubernetes.

Fleet device templates also support the catalogItemRef field as an alternative to the image field for both operating system and application entries. When you use catalogItemRef in a fleet device template, Red Hat Edge Manager resolves the catalog reference to a concrete OCI image URL during fleet-to-device rendering. All devices in the fleet receive the resolved image. For the complete field specification and YAML examples, see Catalog item reference field specification in the Additional resources section.

The following fields in device templates support placeholders:

Table 1.2. Template placeholder fields

FieldPlaceholders supported in

Operating system image

The entire image reference string (for example, registry.example.com/edge/rhel:{{ .metadata.labels.stage }})

Git Config Provider

target revision, path

HTTP Config Provider

URL suffix, path

Inline Config Provider

content, path

When many devices share most of their configuration but differ in a few dimensions such as region or device type, you can use layered configuration and template placeholders to manage them in a single fleet instead of creating duplicate fleet definitions. For patterns and examples, see Fleet organization patterns in the Additional resources section.

To configure a fleet device template so that MicroShift clusters on devices in the fleet auto-register with Red Hat Advanced Cluster Management, see Configuring fleets to auto-register MicroShift clusters in the Additional resources section.

1.1. Mixed-fleet behavior with image-mode and package-mode devices

Fleets can contain devices running in different operating system (OS) modes. When a fleet template includes an OS image target, image-mode and package-mode devices process the delivered spec differently, which affects device update status and rollout progress.

Understanding this behavior helps you diagnose OutOfDate status on package-mode devices and plan fleet organization to avoid rollout issues.

How fleet specs are delivered

When a fleet template includes an OS image target, the Red Hat Edge Manager service renders the full spec and delivers it to all devices in the fleet regardless of OS mode. The service does not filter fleet template content per device. Every device receives the same rendered spec, including the spec.os.image field.

Agent-side atomic spec rejection

Package-mode devices reject the entire delivered spec when it contains a spec.os.image field. The rejection occurs atomically before any reconciler runs, which means the following:

  • No configuration overlays from the new spec are applied.
  • No application workloads from the new spec are updated.
  • The device stays on its previously committed spec.
  • No partial application of the spec occurs.

When the fleet template is updated to deliver a spec without an OS image target, package-mode devices accept and reconcile the new spec normally, including any configuration and application changes.

OutOfDate status on package-mode devices

The Red Hat Edge Manager service marks a package-mode device as OutOfDate when the device’s status.os.image (which is empty on package-mode devices) does not match the spec.os.image delivered from the fleet template. This is expected behavior and does not indicate an error.

Package-mode devices in this state continue to operate on their previously committed configuration. Image-mode devices in the same fleet process the spec normally and update their OS image.

Rollout batch stall limitation

Rollout batches that contain package-mode devices can stall when the fleet template includes an OS image target. Package-mode devices cannot satisfy the OS target, so the batch does not complete until the rollout timeout expires.

This stall does not affect the devices themselves, but it delays rollout progress for subsequent batches. To avoid this limitation, organize fleets so that OS image targets are applied only to fleets containing image-mode devices.

1.2. Resolve mixed-fleet rollout issues for package-mode devices

When a fleet template includes an operating system (OS) image target and the fleet contains package-mode devices, those devices show OutOfDate status and rollout batches can stall. You can resolve this by reorganizing fleets, adjusting the fleet template, or moving devices.

Prerequisites

  • You have the Flight Control CLI installed and are logged in to the Red Hat Edge Manager service.
  • You have identified package-mode devices showing OutOfDate status in a fleet that includes an OS image target in the device template.

Procedure

  1. Choose one of the following recovery options and complete the associated steps:

    Option A: Separate image-mode and package-mode devices into different fleets.

    Use this option when you want to apply OS image targets to image-mode devices while managing package-mode devices with a separate fleet template that does not include OS image settings.

    1. Label your devices to distinguish OS modes. For each device, export the current manifest, add a label, and apply the change. In the following example, replace <device_name> with the name of each device:

      flightctl get device/<device_name> -o yaml > device.yaml

      Edit device.yaml to add an osMode label under metadata.labels:

      metadata:
        labels:
          osMode: image

      Set osMode to image for image-mode devices or package for package-mode devices.

      Apply the updated manifest:

      flightctl apply -f device.yaml
    2. Create separate fleets with matching label selectors. The image-mode fleet includes the OS image target in its template. The package-mode fleet template includes only configuration and application workloads.

    Option B: Remove the OS image target from the fleet template.

    Use this option when OS management is handled per-device or through external tooling, and you want all devices in the fleet to receive only configuration and application specs.

    1. Edit the fleet spec and remove the spec.os.image field from the device template.
    2. Apply the updated fleet spec:

      flightctl apply -f <fleet_spec>.yaml

      Replace <fleet_spec> with the path to your fleet spec file.

    Option C: Move package-mode devices out of the fleet.

    Use this option when most devices in the fleet are image-mode and you want to keep the OS image target in the fleet template.

    1. Update device labels so that package-mode devices no longer match the fleet selector. Export the device manifest, edit the labels, and apply the change:

      flightctl get device/<device_name> -o yaml > device.yaml

      Edit device.yaml to change or add labels so that the device no longer matches the fleet label selector, then apply:

      flightctl apply -f device.yaml

Verification

  • Verify that package-mode devices transition from OutOfDate to UpToDate after the recovery action:

    flightctl get devices --field-selector 'status.capabilities.osMode=package'

    The UPDATED column shows Up-to-date for package-mode devices that are either in a fleet without an OS image target or are individually managed.

1.3. Selecting devices into a fleet by using the CLI

Define a label selector to add devices into a fleet.

Note

Fleet selectors can only match against device labels. If you want to target devices based on hardware, operating systems, or custom agent attributes, you must first map those attributes to labels during enrollment by using label-from-systeminfo. For an example, see Example: fleet selection from system information.

Complete the following tasks:

Procedure

  1. Run the following command to verify that the label selector returns the devices that you want to add to the fleet:

    flightctl get devices -l 'type=pos-terminal,stage=development'
  2. If running the command returns the expected list of devices, you can define a fleet that selects the devices by using the following YAML file:

    apiVersion: flightctl.io/v1beta1
    kind: Fleet
    metadata:
      name: my-fleet
    spec:
      selector:
        matchLabels:
          type: pos-terminal
          stage: development
    # ...
  3. Apply the change by running the following command:

    flightctl apply -f my-fleet.yaml
  4. Check that no devices in the fleet have overlapping selectors by running the following command:

    flightctl get devices -l 'type=pos-terminal,stage=development' -o json | jq -r '.items[].status.conditions[] | select(.type=="MultipleOwners").status'

    If the output shows False for each device, no other fleet selects the same devices. If any device shows True, review your fleet selectors to remove the overlap.

1.4. Configuring fleets to auto-register MicroShift clusters

If you have fleets of devices that are running an operating system image that includes MicroShift, you can configure your fleets to auto-register MicroShift clusters with Red Hat Advanced Cluster Management.

Configuring your device template

To enable auto-registration in a fleet, add configuration to its device template.

If you have not already created the HTTP Repository named acm-registration for the Red Hat Advanced Cluster Management agent registration API (including validationSuffix and httpConfig), create it by following the repository steps in Configuring your device in Managing devices.

Complete the following steps:

Procedure

  1. Add the acm-crd resource configuration, which includes the filePath for your crd.yaml file, your repository, and suffix to your Fleet resource. See the following example:

    apiVersion: flightctl.io/v1beta1
    kind: Fleet
    metadata:
      name: fleet-acm
    spec:
      selector:
        matchLabels:
          fleet: acm
      template:
        spec:
          os:
            image: <your_os_image>
          config:
            - name: acm-crd
              httpRef:
                filePath: /var/local/acm-import/crd.yaml
                repository: acm-registration
                suffix: /agent-registration/crds/v1
  2. Add the acm-import resource configuration with the filePath, repository, and suffix, as shown in the following example:

          config:
            - name: acm-import
              httpRef:
                filePath: /var/local/acm-import/import.yaml
                repository: acm-registration
                suffix: /agent-registration/manifests/{{.metadata.name}}
  3. MicroShift must authenticate to the container registries that host the Red Hat Advanced Cluster Management images. If the device already has a suitable pull secret for those registries (for example, at /etc/crio/openshift-pull-secret), you do not need to add another one here. Otherwise, add the pull-secret resource, as you see in the following addition to the template:

          config:
            - name: pull-secret
              inline:
                - path: "/etc/crio/openshift-pull-secret"
                  content: "{\"auths\":{...}}"
  4. Add the apply-acm-manifests resource. The following hook waits until the kubeadmin kubeconfig file exists and MicroShift pods are ready, then runs kubectl apply on your crd.yaml and import.yaml files when those files are created. The afterupdating hook fires both when a device first joins the fleet and receives its initial configuration, and on subsequent updates.

    Include the wait steps because MicroShift might not be running when the hook runs. For example, you might apply an operating system image update and this registration configuration together in a single device update. See the following example:

          config:
            - name: apply-acm-manifests
              inline:
                - path: "/etc/flightctl/hooks.d/afterupdating/50-acm-registration.yaml"
                  content: |
                    - run: /usr/bin/bash -c "until [ -f $KUBECONFIG ]; do sleep 1; done"
                      timeout: 5m
                      envVars:
                        KUBECONFIG: /var/lib/microshift/resources/kubeadmin/kubeconfig
                    - run: kubectl wait --for=condition=Ready pods --all --all-namespaces --timeout=300s
                      timeout: 5m
                      envVars:
                        KUBECONFIG: /var/lib/microshift/resources/kubeadmin/kubeconfig
                    - if:
                      - path: /var/local/acm-import/crd.yaml
                        op: [created]
                      run: kubectl apply -f /var/local/acm-import/crd.yaml
                      envVars:
                        KUBECONFIG: /var/lib/microshift/resources/kubeadmin/kubeconfig
                    - if:
                      - path: /var/local/acm-import/import.yaml
                        op: [created]
                      run: kubectl apply -f /var/local/acm-import/import.yaml
                      envVars:
                        KUBECONFIG: /var/lib/microshift/resources/kubeadmin/kubeconfig

    Auto-registration uses the Red Hat Advanced Cluster Management agent registration API: devices fetch the Kubernetes manifests for the klusterlet agent and apply them to MicroShift. The resources under spec.template.spec.config in your fleet template do the following:

    • acm-crd uses the HTTP configuration provider to query the agent registration server for manifests that contain the custom resource definition for the Red Hat Advanced Cluster Management klusterlet agent. The Red Hat Edge Manager agent writes those manifests to /var/local/acm-import/crd.yaml on each device.
    • acm-import queries the server for import manifests for a cluster whose name matches the device. The {{.metadata.name}} template variable supplies the device name. The agent writes the returned manifests to /var/local/acm-import/import.yaml.
    • pull-secret adds registry credentials on the device so that MicroShift can pull the Red Hat Advanced Cluster Management agent images. Skip this item if you already provide a suitable pull secret by another method, for example by embedding it in the operating system image or by using another configuration provider.
    • apply-acm-manifests adds an afterupdating lifecycle hook rule file under /etc/flightctl/hooks.d/afterupdating/. The hook runs after the configuration is applied and the crd.yaml and import.yaml files exist; it applies those manifests to MicroShift by using kubectl. For more information, see Device lifecycle hooks.
  5. In the console, label the device fleet:acm and click Approve, which automatically selects the fleet-acm fleet. For more information about labels on devices, see Labels and label selectors.

Chapter 2. Fleet organization patterns

When most device configuration is shared across device types, regions, or deployment stages, you can compose layered configuration within a single fleet definition instead of duplicating fleets for each variation.

2.1. Layered gitRef composition

A fleet’s template spec contains a spec.config list that can hold multiple gitRef entries, each pointing to a different directory in a Git repository. Entries are applied in list order. When two entries write the same file path, the entry that appears later in the list takes precedence.

This composition model lets you structure configuration as layers:

  • A base layer with configuration shared by all devices in the fleet.
  • One or more override layers that vary by device label, such as region, device type, or deployment stage.

The following fleet template shows a base layer and a region-specific override layer:

spec:
  config:
    - name: base-config
      gitRef:
        repository: site-settings
        targetRevision: main
        path: /config/base
    - name: region-config
      gitRef:
        repository: site-settings
        targetRevision: main
        path: /config/regions/{{ .metadata.labels.region }}

The base-config entry delivers files shared by every device. The region-config entry uses a template placeholder to resolve a region-specific directory per device. A device labeled region: eu-west receives files from /config/regions/eu-west, while a device labeled region: us-east receives files from /config/regions/us-east.

2.2. Config entry ordering

Entries in spec.config are applied in the order they appear in the list. If two entries write a file to the same path on the device, the last entry in the list takes precedence. Place override layers after the base layer so that their files replace the base versions.

This ordering rule applies regardless of the provider type. A gitRef entry at position three overrides a conflicting file from an httpRef entry at position two.

2.3. Combining multiple provider types

A fleet template is not limited to gitRef entries. You can combine different provider types in a single spec.config list:

gitRef
Version-controlled configuration files stored in a Git repository.
httpRef
Dynamically generated configuration retrieved from HTTP endpoints.
secretRef
Kubernetes secrets containing credentials or certificates.
inline
Small configuration snippets defined directly in the fleet specification.

Template placeholders work in all provider types. For example, you can use {{ .metadata.labels.site }} in a secretRef name to deliver site-specific credentials to each device based on its site label.

2.4. Using getOrDefault for optional overrides

When a template placeholder references a device label that a device does not have, the placeholder silently produces an empty string.

Note

A bare placeholder such as {{ .metadata.labels.nonexistent }} resolves to an empty string without raising an error. This can cause devices to receive empty paths or configuration values without any warning. Always use getOrDefault for labels that might not be present on every device.

Use the getOrDefault function to provide a meaningful fallback value instead of an empty string:

path: /config/overrides/{{ getOrDefault .metadata.labels "variant" "default" }}

Devices with a variant label receive the configuration directory that matches their label value. Devices without the variant label receive the default directory instead.

2.5. Git repository structure

Organizing your Git repository with a clear directory hierarchy makes the layered composition pattern easier to maintain. The following layout separates base configuration from region-specific and device-type-specific overrides:

site-settings/
  config/
    base/
      motd.conf
      ntp.conf
    regions/
      eu-west/
        ntp.conf
      us-east/
        ntp.conf
    types/
      pos-terminal/
        app.conf
      kiosk/
        app.conf

Each fleet template entry points to a directory at the appropriate level. The base layer points to config/base/, a region override points to config/regions/{{ .metadata.labels.region }}, and a device-type override points to config/types/{{ .metadata.labels.type }}. Files in an override directory replace files from the base layer only when both layers write to the same file path; all other base files remain unchanged.

2.6. Template placeholder functions and expressions

Template placeholders support variable substitution and the upper, lower, replace, and getOrDefault functions. You can chain these functions in pipelines, for example {{ getOrDefault .metadata.labels "key" "default" | upper }}.

Template placeholders also support conditional expressions using if, else, else if, and with blocks, along with the comparison functions eq, ne, lt, gt, le, ge, and, or, and not. Loops (range) are not supported in template placeholders.

Note

Inside a with block, dot (.) is rebound to the value of the with expression. Use $ to access root-level fields such as {{ $.metadata.name }} or {{ $.metadata.labels.key }}. Without the $ prefix, field references resolve against the rebound context and fail server-side validation.

2.7. Fleet design decisions

Choosing between a single fleet with template placeholders and multiple separate fleets depends on how your devices differ in configuration, rollout cadence, and operational ownership. Understanding these factors early prevents unnecessary fleet duplication and simplifies long-term maintenance.

2.7.1. Decision summary

The following table summarizes how each factor influences the choice between a single fleet and multiple fleets.

Table 2.1. Fleet design decision factors

FactorSingle fleetMultiple fleets

Configuration differences

Label-expressible dimensions

Fundamentally different configs

Rollout cadence

Uniform or batch-sequenced

Independent per group

Team ownership

Single team

Different teams

Regulatory separation

Not required

Required

Maintenance overhead

Lower (one definition)

Higher (duplicated definitions)

2.7.2. When a single fleet works well

A single fleet definition with layered configuration is the simplest approach when devices share more configuration than they differ. The following factors favor consolidating devices into one fleet:

  • Configuration differences are limited to a small number of dimensions, such as region, site, device type, or deployment stage, that you can express as device labels.
  • You want a unified rollout so that one template update reaches all devices through a single rollout policy.
  • Template placeholders, getOrDefault, and conditional expressions (if, else, with) cover all per-device variation.

When these conditions hold, a single fleet keeps configuration in one place and reduces the risk of drift between groups of devices.

2.7.3. When multiple fleets are appropriate

Separate fleet definitions provide stronger boundaries between groups of devices. The following factors favor splitting devices across multiple fleets:

  • Groups of devices run fundamentally different operating system images, such as different Red Hat Enterprise Linux major versions or different base images for different hardware platforms.
  • Different teams own different sets of devices and need independent control over template changes and rollout timing.
  • Groups of devices require different rollout cadences. For example, factory controllers might update weekly while retail terminals update monthly.
  • Regulatory or compliance requirements mandate separation between device populations. Separate fleets provide an auditable boundary that a label-based distinction within a single fleet does not.

Each additional fleet definition adds maintenance overhead because shared configuration must be kept in sync across fleet resources. Weigh the benefit of isolation against the cost of duplication before creating separate fleets.

2.7.4. Namespace separation versus fleet separation

Fleets and namespaces address different levels of separation. A fleet controls which devices share the same desired configuration and rollout policy. A namespace (or OpenShift project) controls access boundaries — which teams or service accounts can view and manage resources.

Use separate namespaces when different teams need independent access control over their fleets, secrets, and repositories. Use separate fleets within the same namespace when devices need different configuration or rollout cadences but the same team manages them. Combine both when teams manage distinct device populations that also require independent configuration — for example, one namespace per region with multiple fleets per device role within each namespace.

2.7.5. Canary rollout strategies

You can implement canary rollouts within a single fleet or across separate fleets. Each approach has different trade-offs for isolation and configuration management.

Within a single fleet

Use rolloutPolicy with deviceSelection and a BatchSequence strategy to target canary devices first. Label a subset of devices with a canary label, for example stage: canary, and define the first batch with a selector that matches that label. Set a successThreshold, such as "80%", so the rollout pauses if too many canary devices fail. This approach keeps all configuration in one fleet definition and avoids duplication.

With separate fleets

Create a canary fleet with a narrow label selector, such as stage: canary, and a production fleet with the broader selector. Apply configuration changes to the canary fleet first. After you validate the changes on canary devices, apply the same changes to the production fleet. This approach provides stronger isolation because a failed canary rollout cannot affect production devices. It also lets you test changes independently without rolling them out to production, and separates canary device status from production device status. However, it duplicates configuration across two fleet definitions.

2.7.6. Rollout policy scope

The rollout policy applies to the entire fleet template spec. When any configuration entry changes, or when the system detects an upstream change in a Git repository or HTTP endpoint referenced by the fleet, the rollout covers all devices in the fleet.

You cannot roll out changes to one configuration concern independently of another concern within the same fleet. For example, if both network settings and application configuration are defined in the same fleet’s template spec, a change to network settings triggers a rollout that updates the desired state for all devices, including the unchanged application configuration.

If independent rollout cadences per concern are required, separate those concerns into different fleets. Each fleet then has its own rollout policy and its own rollout lifecycle.

2.8. Consolidating duplicate fleet definitions

Migrate from multiple near-identical fleet definitions to a single fleet that uses template placeholders and layered configuration to eliminate duplication.

Prerequisites

  • You have the flightctl CLI installed.
  • You have at least two fleet definitions with overlapping configuration.
  • You have access to the Git repository that stores your device configuration.

Procedure

  1. Export fleet definitions and compare their configuration entries:

    flightctl get fleets -o yaml > all-fleets.yaml

    Review the output and identify configuration entries that are identical or nearly identical across fleets. Note which entries differ and what dimension, such as region, device type, or site, drives the difference.

  2. Restructure the Git repository to separate shared configuration from per-dimension overrides. For example:

    site-settings/
      config/
        base/
          motd.conf
          ntp.conf
        regions/
          eu-west/
            ntp.conf
          us-east/
            ntp.conf

    Move shared files to the base/ directory. Move files that vary by dimension into the corresponding override directories.

  3. Decide which device labels encode the varying dimensions. For each dimension that differs across your current fleets, assign a label key. For example, if fleets differ by region, use a region label with values such as eu-west and us-east.
  4. Label each device with the appropriate dimension values. Edit the device resource and add the required labels under metadata.labels:

    flightctl edit device/<device_name>

    Alternatively, export the device definition, edit the YAML file, and apply it:

    flightctl get device/<device_name> -o yaml > device.yaml

    Edit device.yaml to add or update labels, then apply the changes:

    flightctl apply -f device.yaml
  5. Create a consolidated fleet definition that uses multiple gitRef entries with template placeholders:

    apiVersion: flightctl.io/v1beta1
    kind: Fleet
    metadata:
      name: pos-terminals
    spec:
      selector:
        matchLabels:
          type: pos-terminal
      template:
        metadata:
          labels:
            type: pos-terminal
        spec:
          os:
            image: quay.io/example/pos-rhel:9.5
          config:
            - name: base-config
              gitRef:
                repository: site-settings
                targetRevision: main
                path: /config/base
            - name: region-config
              gitRef:
                repository: site-settings
                targetRevision: main
                path: /config/regions/{{ .metadata.labels.region }}
  6. Select one device from each dimension and verify that the consolidated fleet delivers the correct rendered configuration. All config providers are merged into a single rendered configuration. Review the rendered spec to confirm that files from both the base and override layers are present:

    flightctl get device/<device_name> --rendered -o json | jq '.spec.config'
  7. Update device labels so that devices match the consolidated fleet selector and no longer match the old fleet selectors. Devices automatically leave their old fleet and join the new fleet when their labels match the new selector.

    Important

    If a device temporarily matches selectors for both an old fleet and the new fleet, Red Hat Edge Manager sets the MultipleOwners condition to true on the device. The device remains in its current fleet until the conflicting selector is resolved. Delete old fleet definitions or adjust their selectors to avoid this overlap.

  8. After all devices have migrated to the consolidated fleet and you have verified their status, delete the old fleet definitions:

    flightctl delete fleet/<old_fleet_name>

Verification

  • Run flightctl get devices -l 'type=pos-terminal' and verify that all devices show the expected OWNER value, which is the consolidated fleet name.
  • Run flightctl get device/<device_name> -o yaml for a device from each dimension and verify the rendered configuration matches expectations.
  • Check that no devices have the MultipleOwners condition set to true:

    flightctl get devices -o json | jq '.items[] | select(.status.conditions[]? | select(.type=="MultipleOwners" and .status=="True")) | .metadata.name'

2.9. Fleet organization examples

The following examples show common fleet organization patterns using layered configuration and template placeholders to reduce fleet duplication.

2.9.1. Multi-region fleet

A fleet of point-of-sale terminals deployed across multiple regions. All terminals share base OS tuning and application configuration, but NTP servers and network settings differ by region. A single fleet definition with a region-specific override layer eliminates the need to create a separate fleet for each region.

apiVersion: flightctl.io/v1beta1
kind: Fleet
metadata:
  name: pos-terminals
spec:
  selector:
    matchLabels:
      type: pos-terminal
  template:
    metadata:
      labels:
        type: pos-terminal
    spec:
      os:
        image: quay.io/example/pos-rhel:9.5
      config:
        - name: base-config
          gitRef:
            repository: site-settings
            targetRevision: main
            path: /config/base
        - name: region-config
          gitRef:
            repository: site-settings
            targetRevision: main
            path: /config/regions/{{ .metadata.labels.region }}

Devices need the following labels: type: pos-terminal and region: eu-west (or us-east, ap-south, and so on). The region-config entry resolves a different directory for each device based on its region label.

The Git repository separates base files from region-specific overrides:

site-settings/
  config/
    base/
      motd.conf
      ntp.conf
      app.conf
    regions/
      eu-west/
        ntp.conf
      us-east/
        ntp.conf
      ap-south/
        ntp.conf

2.9.2. Mixed device types sharing base configuration

A fleet includes both point-of-sale terminals and self-service kiosks that share base monitoring and logging configuration but run different applications. A single fleet definition avoids duplicating the shared monitoring configuration across separate fleet resources.

apiVersion: flightctl.io/v1beta1
kind: Fleet
metadata:
  name: store-devices
spec:
  selector:
    matchLabels:
      environment: store
  template:
    metadata:
      labels:
        environment: store
    spec:
      os:
        image: quay.io/example/store-rhel:9.5
      config:
        - name: base-monitoring
          gitRef:
            repository: site-settings
            targetRevision: main
            path: /config/base
        - name: device-type-config
          gitRef:
            repository: site-settings
            targetRevision: main
            path: /config/types/{{ .metadata.labels.type }}

Devices need the following labels: environment: store and type: pos-terminal or type: kiosk. The device-type-config entry delivers application-specific configuration files based on the device type label, while every device receives the shared monitoring and logging files from the base layer.

The Git repository groups shared configuration separately from device-type-specific application configuration:

site-settings/
  config/
    base/
      logging.conf
      monitoring.conf
    types/
      pos-terminal/
        app.conf
      kiosk/
        app.conf

2.9.3. Staged rollout with canary batch

A fleet uses rolloutPolicy to deploy changes to canary devices first, then to the remaining production devices. This pattern reduces the risk of a faulty update reaching all devices at once.

apiVersion: flightctl.io/v1beta1
kind: Fleet
metadata:
  name: factory-controllers
spec:
  selector:
    matchLabels:
      role: factory-controller
  rolloutPolicy:
    deviceSelection:
      strategy: BatchSequence
      sequence:
        - selector:
            matchLabels:
              stage: canary
          successThreshold: "80%"
        - selector: {}
  template:
    metadata:
      labels:
        role: factory-controller
    spec:
      os:
        image: quay.io/example/factory-rhel:9.5
      config:
        - name: controller-config
          gitRef:
            repository: factory-settings
            targetRevision: main
            path: /config/controllers

Devices need the following labels: role: factory-controller and, for canary devices only, a label such as stage: canary. The label key and value are examples; use whichever label convention fits your environment. Production devices do not need the stage label.

The first batch targets devices labeled stage: canary. After 80% of canary devices report success, the second batch with an empty selector targets all remaining devices in the fleet.

2.9.4. Combined gitRef and secretRef for per-site credentials

Devices at different sites need site-specific network credentials stored as Kubernetes secrets, alongside shared application configuration from a Git repository. Combining gitRef and secretRef in a single fleet avoids creating separate fleet definitions for each site.

apiVersion: flightctl.io/v1beta1
kind: Fleet
metadata:
  name: site-devices
spec:
  selector:
    matchLabels:
      managed: "true"
  template:
    metadata:
      labels:
        managed: "true"
    spec:
      os:
        image: quay.io/example/site-rhel:9.5
      config:
        - name: base-app-config
          gitRef:
            repository: app-config
            targetRevision: main
            path: /config/app
        - name: site-credentials
          secretRef:
            name: network-creds-{{ .metadata.labels.site }}
            namespace: flightctl
            mountPath: /etc/network-credentials

Devices need the following labels: managed: "true" and site: factory-berlin (or factory-madrid, and so on). The secretRef name resolves per device, so a device labeled site: factory-berlin receives the secret network-creds-factory-berlin.

Legal Notice

Copyright © Red Hat.
Except as otherwise noted below, the text of and illustrations in this documentation are licensed by Red Hat under the Creative Commons Attribution–Share Alike 3.0 Unported license . If you distribute this document or an adaptation of it, you must provide the URL for the original version.
Red Hat, as the licensor of this document, waives the right to enforce, and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent permitted by applicable law.
Red Hat, the Red Hat logo, JBoss, Hibernate, and RHCE are trademarks or registered trademarks of Red Hat, LLC. or its subsidiaries in the United States and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other countries.
XFS is a trademark or registered trademark of Hewlett Packard Enterprise Development LP or its subsidiaries in the United States and other countries.
The OpenStack® Word Mark and OpenStack logo are trademarks or registered trademarks of the Linux Foundation, used under license.
All other trademarks are the property of their respective owners.