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
kubeconfigfile. -
kubectlif 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:
- Apply the same label to the gateways you want to group.
- 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-1role=video-ingestos-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:
- Log in to the Izuma Device Management Portal and go to Container management > Nodes.
- Select the gateways you want to include in the group.
- Select Actions > Label selected nodes.
- Enter the label as
key=value, for exampleos-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/siteorsite. - 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/andk8s.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.nodeSelectordetermines which gateways receive the workload. Every label in the selector must match. In this example,os-distro: almalinuxtargets every gateway labeledos-distro=almalinux.Remove
nodeSelectorentirely if you want to deploy the workload to every gateway in the account. -
spec.selector.matchLabelsmust matchspec.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.maxUnavailablecontrols how many gateways can be unavailable while an update is rolled out. A value of1updates the group one gateway at a time. For larger fleets, you can increase this value to deploy updates more quickly. -
NODE_NAMEuses 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
-
Go to Container management > Workloads and select Deploy a workload.
-
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. -
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.
- Set Nodes updated at a time to the desired
maxUnavailablevalue. - 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:
- Which gateway is not running the workload?
- 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.
revisionHistoryLimitdefaults to10, 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-ingestover labels tied to temporary implementation details such ashardware=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,
Readymay be lower thanDesiredbecause some gateways are temporarily offline. Investigate the difference, but do not automatically treat every offline gateway as a failed deployment. -
Start with a low
maxUnavailablevalue. 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-basednodeAffinityexpressions such asInandNotInare 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 to10. You cannot roll back to revisions older than the retained history.