Mistake on this page? Email us

Deploy a workload to a group of gateways

Deploying containerized applications explains how to run a Pod on a specific gateway.

When you need to deploy the same workload to multiple gateways, you can group gateways using labels and target the group with a DaemonSet. This lets you:

  • Deploy the workload to every gateway in the group.
  • Automatically deploy to new gateways when they join the group.
  • Track rollout progress across the group.
  • Identify and troubleshoot gateways where the workload is not running.

You can complete all of these steps in the Izuma Device Management Portal or with kubectl. Both methods manage the same Kubernetes objects, so you can use whichever is most convenient at each step.

Prerequisites

Before you begin, make sure you have:

  • An Izuma account with administrator permissions and Kubernetes-as-a-Service (KaaS) enabled. If you have not used container orchestration before, follow Getting started to create an access key and kubeconfig file.

  • kubectl if you plan to use the command line.

  • At least two gateways running Izuma Edge that are connected and Ready.

How gateway groups work

You do not need to create a separate Kubernetes object to represent a gateway group.

Instead, you:

  1. Apply the same label to the gateways you want to group.
  2. Configure a workload to target gateways with that label.

Two Kubernetes objects are commonly used:

Object What it does When to use it
Pod with nodeName Runs a Pod on one specific gateway. If that gateway becomes unavailable, the Pod does not move elsewhere. Testing or running a workload on one specific gateway.
DaemonSet with nodeSelector Runs one copy of the Pod on every gateway whose labels match the selector. Deploying a workload to a group of gateways.

A key benefit of a DaemonSet is that Kubernetes continuously evaluates which gateways belong to the group.

For example:

  • If you provision a new gateway later and give it the matching label, the workload is deployed automatically.
  • If you remove the label from a gateway, Kubernetes removes the workload from that gateway.
  • If a gateway is offline when you deploy, it remains part of the target group and starts the workload when it reconnects.

Because labels define long-lived deployment groups, choose labels that describe meaningful characteristics of your fleet. For example:

  • site=helsinki-1
  • role=video-ingest
  • os-distro=almalinux

Step 1: Group gateways with labels

Labels available by default

Izuma Edge automatically sets several labels on every node. You can use these labels for targeting without adding your own.

Label Example value
beta.kubernetes.io/arch amd64, arm64
beta.kubernetes.io/os linux
kubernetes.io/hostname The node ID, for example 01a039d084985a8313973ae700000000

Note: Izuma Edge nodes use the beta.kubernetes.io/ versions of the architecture and operating system labels. The newer kubernetes.io/arch and kubernetes.io/os labels are not set. A nodeSelector using the newer labels will therefore match no gateways. Use the beta. versions instead.

Add your own labels

Add labels that describe stable characteristics of your gateways, such as:

  • Site or location
  • Hardware type
  • Operating system distribution
  • Gateway role

For example:

os-distro=almalinux

To add labels in the Portal:

  1. Log in to the Izuma Device Management Portal and go to Container management > Nodes.
  2. Select the gateways you want to include in the group.
  3. Select Actions > Label selected nodes.
  4. Enter the label as key=value, for example os-distro=almalinux, and select Apply.

The label is applied to all selected gateways and appears in the Labels column.

To add a label with kubectl:

$ kubectl label node 01a039d084985a8313973ae700000000 os-distro=almalinux

node/01a039d084985a8313973ae700000000 labeled

To update an existing label, add --overwrite.

To remove a label, add a trailing hyphen to the label key:

$ kubectl label node 01a039d084985a8313973ae700000000 os-distro-

node/01a039d084985a8313973ae700000000 unlabeled

Verify the group before deploying

Before deploying a workload, confirm that the label matches exactly the gateways you expect.

This is a simple way to catch label mistakes before they affect a deployment.

$ kubectl get nodes -l os-distro=almalinux

NAME                               STATUS     ROLES    AGE     VERSION
01a039d084985a8313973ae700000000   Ready      <none>   3d22h   v1.13.2-argus
01a03ba53add5a8313973ae700000000   NotReady   <none>   3d17h   v1.13.2-argus
01a03c1e0dc55a8313973ae700000000   Ready      <none>   3d15h   v1.13.2-argus
01a03c3a6a9e5a8313973ae700000000   Ready      <none>   3d14h   v1.13.2-argus

In this example, four gateways belong to the group and one is currently offline.

Offline gateways are normal in many edge environments. It is useful to identify them before deployment because they affect the rollout counters shown later.

To display a label as a column across the fleet:

$ kubectl get nodes -L os-distro

In the Portal, the Nodes page displays each gateway's labels. You can also use the filter bar to show only gateways with a specific label.

Label rules and recommendations

Keep the following rules in mind when creating labels:

  • A label key can contain an optional prefix and a name, such as example.com/site or site.
  • Label names and values can be up to 63 characters.
  • Keys and values can contain letters, numbers, -, _, and ., and must begin and end with an alphanumeric character.
  • The kubernetes.io/ and k8s.io/ prefixes are reserved. Do not create your own labels under these prefixes because some values are managed by the gateway and may be overwritten.
  • A gateway can have multiple labels. For example:
site=helsinki-1
hardware=rpi
os-distro=almalinux

Step 2: Create the workload manifest

The following example deploys a small heartbeat container to every gateway in the target group.

The example uses a multi-architecture image so it can run on both amd64 and arm64 gateways. The container writes a heartbeat message to its log every ten seconds.

Save the following manifest as heartbeat.yaml:

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: heartbeat
  labels:
    app: heartbeat
spec:
  selector:
    matchLabels:
      app: heartbeat

  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1

  template:
    metadata:
      labels:
        app: heartbeat

    spec:
      automountServiceAccountToken: false

      nodeSelector:
        os-distro: almalinux

      containers:
      - name: heartbeat
        image: busybox:1.36

        env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName

        command: ["/bin/sh", "-c"]
        args:
        - |
          i=0
          while true; do
            i=$((i+1))
            echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) heartbeat #$i node=$NODE_NAME arch=$(uname -m)"
            sleep 10
          done

The following fields are especially important when targeting a gateway group:

  • spec.template.spec.nodeSelector determines which gateways receive the workload. Every label in the selector must match. In this example, os-distro: almalinux targets every gateway labeled os-distro=almalinux.

    Remove nodeSelector entirely if you want to deploy the workload to every gateway in the account.

  • spec.selector.matchLabels must match spec.template.metadata.labels. This selector tells the DaemonSet which Pods it manages. It does not select gateways and cannot be changed after the DaemonSet is created.

  • updateStrategy.rollingUpdate.maxUnavailable controls how many gateways can be unavailable while an update is rolled out. A value of 1 updates the group one gateway at a time. For larger fleets, you can increase this value to deploy updates more quickly.

  • NODE_NAME uses the Kubernetes downward API to populate the current gateway's node name. This allows each instance of the workload to identify the gateway on which it is running.

Note: The container image must support the architecture of every gateway in the group. If the group contains both amd64 and arm64 gateways, use a multi-architecture image. Alternatively, include beta.kubernetes.io/arch in the nodeSelector and deploy separate workloads for each architecture.

Step 3: Deploy the workload

Using the Portal

  1. Go to Container management > Workloads and select Deploy a workload.

  2. On the Application step, either:

    • Enter the application name, image, and port, or
    • Select Paste a manifest and paste the contents of heartbeat.yaml.

    The Portal accepts YAML or JSON. For group deployments, the manifest must define a DaemonSet.

  3. On the Targets step, select Deploy to > Nodes with a label, then choose:

os-distro=almalinux

You can also target:

  • All nodes
  • Nodes of an architecture
  • Specific nodes

The Portal shows how many gateways currently match your selection and warns you if any matching gateways are not Ready.

Note: Targeting and update settings selected in the Portal override the corresponding settings in a pasted manifest. The Review step shows any values that were overridden.

  1. Set Nodes updated at a time to the desired maxUnavailable value.
  2. Review the final manifest on the Review step and select Deploy.

Using kubectl

$ kubectl apply -f heartbeat.yaml

daemonset.apps/heartbeat created

Step 4: Monitor the rollout

A DaemonSet reports rollout progress using several counters.

The Portal displays these counters on the workload page. You can view the same information with kubectl:

$ kubectl get daemonsets

NAME        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR          AGE
heartbeat   4         4         3       4            3           os-distro=almalinux    21h
Counter Portal Meaning
desiredNumberScheduled Desired Gateways whose labels match the nodeSelector, including gateways that are currently offline.
currentNumberScheduled Scheduled Gateways that currently have a Pod assigned.
numberReady Ready Pods that are running and passing their readiness checks.
updatedNumberScheduled Updated Pods running the current version of the workload. During an update, this shows how many gateways have received the new version.
numberAvailable Available Pods that have been ready long enough to count as available.
numberUnavailable Unavailable Targeted gateways that do not currently have an available Pod.
numberMisscheduled Misscheduled Pods running on gateways that no longer match the nodeSelector. Kubernetes normally removes these automatically. Investigate if this value remains non-zero.

In the example above:

DESIRED 4
READY   3

This means four gateways belong to the group, but only three currently have a ready workload.

In this example, the fourth gateway is offline. Because it still belongs to the target group, it is included in DESIRED. The workload starts automatically when the gateway reconnects.

The Portal also summarizes rollout status on the Workloads page:

State Meaning
Ready Every targeted gateway is running the workload.
In progress The rollout is still being applied across the group.
Not ready Every targeted gateway has the current workload version, but one or more Pods are not ready.
No matching nodes No gateways match the selected labels. This is commonly caused by a label mismatch or typo.
Pending The deployment has been accepted, but the controller has not processed it yet.

To wait for a rollout from a terminal or script:

$ kubectl rollout status daemonset/heartbeat

daemon set "heartbeat" successfully rolled out

If some gateways cannot become available, the command reports what it is waiting for:

$ kubectl rollout status daemonset/heartbeat --timeout=60s

Waiting for daemon set "heartbeat" rollout to finish: 3 of 4 updated pods are available...
error: timed out waiting for the condition

Tip: Always specify --timeout when using kubectl rollout status in automation. An edge gateway may remain offline for an extended period, so waiting indefinitely can cause scripts to hang.

Step 5: Troubleshoot a workload that is not fully ready

When a rollout is incomplete, start with the group and work down to the individual gateway.

The goal is to answer two questions:

  1. Which gateway is not running the workload?
  2. Why?

Identify the affected gateway

List the workload's Pods and the gateways on which they are running:

$ kubectl get pods -l app=heartbeat -o wide

NAME              READY   STATUS    RESTARTS   AGE   IP            NODE                               NOMINATED NODE
heartbeat-6gxlt   1/1     Running   0          21h   172.21.2.14   01a039d084985a8313973ae700000000   <none>
heartbeat-cdg2s   0/1     Pending   0          20h   <none>        01a03ba53add5a8313973ae700000000   <none>
heartbeat-czbbk   1/1     Running   0          20h   172.21.2.20   01a03c1e0dc55a8313973ae700000000   <none>
heartbeat-lv8d5   1/1     Running   0          20h   172.21.2.14   01a03c3a6a9e5a8313973ae700000000   <none>

In the Portal, the workload's Pods tab shows the same information with a status indicator for each Pod.

The Nodes page also shows which gateways are currently Ready.

Check gateway status

A Pod that remains Pending and has no IP address is often waiting for a gateway that is not connected.

Check the gateway:

$ kubectl get node 01a03ba53add5a8313973ae700000000

NAME                               STATUS     ROLES    AGE     VERSION
01a03ba53add5a8313973ae700000000   NotReady   <none>   3d17h   v1.13.2-argus

A NotReady gateway indicates a gateway connectivity or availability issue rather than a workload configuration issue.

Check the gateway's connectivity under Device directory in the Portal and see Managing devices for gateway-side diagnostics.

You do not need to redeploy the workload. The Pod starts automatically when the gateway reconnects.

View workload logs

Use kubectl logs to view recent logs:

$ kubectl logs -l app=heartbeat --tail=2

2026-08-29T18:41:10Z heartbeat #7536 node=01a03c3a6a9e5a8313973ae700000000 arch=x86_64
2026-08-29T18:41:20Z heartbeat #7537 node=01a03c3a6a9e5a8313973ae700000000 arch=x86_64

Logs are retrieved from the gateway on demand.

If a gateway is disconnected, the request may return an error such as:

Error from server: Get http://01a03ba53add5a8313973ae700000000:10250/containerLogs/default/heartbeat-cdg2s/heartbeat: read tcp ...: connection reset by peer

This indicates that the gateway is unreachable and is consistent with the node being NotReady.

In the Portal, go to Container management > Pods, open the Pod, and select the Logs tab.

Check Kubernetes events

To view recent events:

$ kubectl get events --sort-by=.lastTimestamp

The same events are available in the Portal under Container management > Events.

Common events include:

Reason Meaning
Started The container started successfully.
BackOff The container exited and Kubernetes is restarting it with an increasing delay. Check the container logs for the cause.
Unhealthy A readiness or liveness probe failed. The event message contains details about the failed probe.

Common issues

Symptom Likely cause What to check
Desired is 0 and nothing is deployed The nodeSelector does not match any gateways Run kubectl get nodes -l <key>=<value> and verify the label key and value. Even a small typo results in an empty group without producing an error.
Desired is lower than expected Some gateways do not have the required label Compare kubectl get nodes -L <key> with the gateways you intended to include.
Pod is Pending and gateway is NotReady The gateway is offline Check gateway connectivity. No workload change is required.
Pod is ImagePullBackOff The container image cannot be pulled Verify the image name and tag, architecture support, and registry credentials. See Managing and hosting container images.
Pod is CrashLoopBackOff The container starts and then exits Run kubectl logs <pod> --previous.
Pod runs on one architecture but not another The image supports only one architecture Use a multi-architecture image or deploy separate workloads using beta.kubernetes.io/arch.
Misscheduled remains non-zero A Pod is still running on a gateway that no longer matches Check the gateway's current labels and connectivity. Kubernetes cannot remove the Pod while the gateway is unreachable.

Step 6: Add or remove gateways from the group

You can change group membership without changing or redeploying the workload.

Add a gateway

Apply the group's label to the gateway:

$ kubectl label node 01a03c3a6a9e5a8313973ae700000000 os-distro=almalinux

node/01a03c3a6a9e5a8313973ae700000000 labeled

A Pod is created on the gateway automatically.

You can verify it with:

$ kubectl get pods -l app=heartbeat -o wide

Remove a gateway

Remove the group's label:

$ kubectl label node 01a03c3a6a9e5a8313973ae700000000 os-distro-

node/01a03c3a6a9e5a8313973ae700000000 unlabeled

Kubernetes removes the workload's Pod from that gateway.

Important: Removing a label removes the Pod immediately. maxUnavailable controls rolling updates, but it does not limit how quickly gateways are removed from a group. If you are removing many gateways, remove labels in batches sized according to the amount of simultaneous workload loss you can tolerate.

Update the workload

To change the workload itself, modify the manifest and apply it again:

$ kubectl apply -f heartbeat.yaml --record=true

daemonset.apps/heartbeat configured

Then monitor the rollout:

$ kubectl rollout status daemonset/heartbeat --timeout=300s

The rollout follows the configured maxUnavailable value.

Note: --record=true stores the command that caused the change in the kubernetes.io/change-cause annotation. This information appears in the CHANGE-CAUSE column of the rollout history. Without it, Kubernetes records the revision but not the command that created it.

Remove the workload

To remove the workload from every gateway in the group:

$ kubectl delete daemonset heartbeat

daemonset.apps "heartbeat" deleted

Step 7: Roll back a workload update

Kubernetes keeps previous versions of the workload's Pod template as revisions.

If an update causes a problem, you can roll back to an earlier revision without recreating the previous manifest manually.

View revision history

$ kubectl rollout history daemonset heartbeat

daemonset.extensions/heartbeat

REVISION  CHANGE-CAUSE
1         kubectl apply --filename=heartbeat.yaml --record=true
2         kubectl apply --filename=heartbeat.yaml --record=true

Roll back to the previous revision

$ kubectl rollout undo daemonset heartbeat

daemonset.extensions/heartbeat rolled back

Roll back to a specific revision

$ kubectl rollout undo daemonset heartbeat --to-revision=1

daemonset.extensions/heartbeat rolled back

Add --dry-run to either command to preview the Pod template without applying the rollback.

In the Portal, the History tab shows the same revisions. You can compare the selected revision with the currently running version and start a rollback directly from the Portal.

Keep the following behavior in mind when rolling back:

  • A rollback creates a new revision. Kubernetes restores the older Pod template as the newest revision rather than rewinding the revision history. As a result, running rollback twice does not necessarily move back two versions.

  • A rollback uses the normal rolling update process. It respects maxUnavailable, just like any other workload update. Gateways that are offline receive the rolled-back version when they reconnect.

  • Revision history is limited. revisionHistoryLimit defaults to 10, so only the most recent ten revisions are available for rollback. Revision history should not be treated as a complete audit trail.

Tip: Some revisions may differ only in small configuration fields while using the same image and ports. Use the Portal's History tab to compare revisions when you need to understand exactly what will change during a rollback.

Best practices

  • Use labels that describe the gateway's role in your deployment. Prefer durable labels such as role=video-ingest over labels tied to temporary implementation details such as hardware=rpi4-batch-3.

  • Always verify the target group before deploying. Use:

    kubectl get nodes -l <key>=<value>
    

    or the label filter on the Portal's Nodes page. A deployment to an empty group can succeed without running anything.

  • Expect offline gateways to appear in rollout counters. In an edge fleet, Ready may be lower than Desired because some gateways are temporarily offline. Investigate the difference, but do not automatically treat every offline gateway as a failed deployment.

  • Start with a low maxUnavailable value. For a new workload or significant update, roll out to a small number of gateways at a time. Increase the value once you are confident in the change.

  • Use one workload for a group rather than one workload per gateway. If a DaemonSet targets a single kubernetes.io/hostname, consider using a Pod instead or defining a broader group.

Limitations

  • Targeting supports nodeSelector-style matching, where every specified label must be present with the expected value. Set-based nodeAffinity expressions such as In and NotIn are not supported by the Portal's targeting workflow.

  • Kubernetes can only start or stop workloads on gateways it can reach. Changes made while a gateway is offline are applied when it reconnects.

  • Kubernetes keeps only the number of revisions configured by revisionHistoryLimit, which defaults to 10. You cannot roll back to revisions older than the retained history.