Continuum finds, validates, and patches code vulnerabilities: it proves which findings are actually exploitable in your environment, ranks them by business impact, and proposes fixes — with human approval before anything ships.
Kubernetes is a control plane that assumes its nodes are reachable, its etcd is durable, and its operator is a person with a laptop and a VPN. Move it to a factory floor, a cell tower, a retail back room, or a ship, and every one of those assumptions breaks. The network lies. The power blinks. The hardware is small. And the person who deployed it is three hundred kilometers away, probably asleep.
This article is a field guide to running Kubernetes at the edge with the tools that actually work there: K3s as the runtime, Fleet as the fleet manager, GitOps as the delivery mechanism, and a set of practices for surviving the conditions the cloud never has to think about. It is written from the operator's side — what you deploy, what breaks, and what to watch.
The word edge is used for three different things, and conflating them is the first mistake.
The architecture changes with the tier. A far-edge site runs one K3s node with a handful of pods and a local store-and-forward buffer. A near-edge site runs a small HA cluster. A device-edge deployment is often not Kubernetes at all — it is a container on a device, managed by the same control plane that manages the clusters.
This article focuses on the far and near edge, where Kubernetes earns its keep: many sites, one control plane, declarative configuration, and the ability to roll out a change to a thousand locations without a plane ticket.
K3s is the right answer for most edge cases, and the reasons are not the ones usually cited.
It is not "Kubernetes lite" in the sense of a toy. It is a certified Kubernetes distribution with the API server, scheduler, controller manager, and kubelet in a single binary, with SQLite as the default datastore and an embedded containerd. The footprint is small — roughly 50 MB binary, ~250 MB RAM at idle — and it runs on a Raspberry Pi, an industrial PC, or a VM with two cores.
The reasons that matter at the edge:
The trade-offs: K3s ships with Traefik, CoreDNS, and a local-path provisioner by default. For edge you usually want to disable Traefik and bring your own ingress, and you want to be careful with the local-path provisioner because local storage on edge hardware is unreliable.
# Minimal edge install: no Traefik, no bundled metrics-server, air-gap friendly
curl -sfL https://get.k3s.io | sh -s - \
--disable traefik \
--disable metrics-server \
--write-kubeconfig-mode 644
# Verify
k3s kubectl get nodes -o wide
systemctl status k3s --no-pager
The edge pattern that works is a hub-and-spoke topology. A single management cluster — running in your cloud or on-prem data center — holds the desired state for every edge site. Each edge site runs its own K3s cluster. The management cluster does not run the edge workloads; it runs the controllers that push configuration to them.
The tool for this is Fleet, Rancher's GitOps engine, though Argo CD and Flux work too. Fleet is purpose-built for the "many clusters, one source of truth" problem: it watches a Git repository, bundles the manifests, and rolls them out to a set of target clusters selected by labels.
The reason Fleet fits the edge better than the alternatives:
# fleet.yaml at the root of your GitOps repo
defaultNamespace: edge-apps
helm:
repo: https://charts.example.com
chart: edge-agent
version: "1.4.2"
values:
telemetryEndpoint: https://telemetry.example.com
targetCustomizations:
- name: retail
clusterSelector:
matchLabels:
site-type: retail
helm:
values:
storeId: "{{ .ClusterLabels.storeId }}"
- name: factory
clusterSelector:
matchLabels:
site-type: factory
helm:
values:
modbusEnabled: true
The cluster selector is the key. You label each edge cluster when it registers — site-type, region, hardware-generation, customer — and Fleet routes the right configuration to the right sites. A new site that registers with the right labels gets the right config automatically. No ticket, no manual step.
At the edge, GitOps is not a preference. It is the only delivery mechanism that survives the conditions.
The reason is simple: the edge cluster cannot be reached. SSH is blocked, the VPN is down, and the person with the keys is on a plane. The only channel that works reliably is outbound HTTPS from the edge to the management plane (or to a Git host). GitOps turns that channel into the entire delivery path: the edge cluster pulls its desired state, applies it, and reports status back. No inbound access required.
The rules that make this work:
latest to the edge. Pin chart versions, image digests, and Fleet bundle versions. An uncontrolled update that breaks one site breaks a thousand.# Fleet rollout with canary
rolloutStrategy:
maxUnavailable: 5%
maxUnavailablePartitions: 1
autoPartitionSize: 10%
partitions:
- name: canary
maxUnavailable: 1
clusterSelector:
matchLabels:
canary: "true"
- name: production
maxUnavailable: 5%
The network at the edge is not slow. It is intermittent, asymmetric, and hostile. Cellular backhaul drops packets. Satellite links have 600 ms latency. Factory networks are air-gapped by policy. The design has to assume the link is down most of the time and treat connectivity as a bonus.
Practices that matter:
# K3s air-gap: private registry mirror config
# /etc/rancher/k3s/registries.yaml
mirrors:
docker.io:
endpoint:
- "https://registry.edge.local:5000"
"registry.edge.local:5000":
endpoint:
- "https://registry.edge.local:5000"
configs:
"registry.edge.local:5000":
tls:
insecure_skip_verify: false
auth:
username: edge
password: "${REGISTRY_PASSWORD}"
You cannot run a full Prometheus + Grafana + Loki + Tempo stack on a 2-core, 4 GB edge node. The observability footprint has to be proportional to the hardware.
The pattern that works:
# VictoriaMetrics agent on the edge
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: vmagent
namespace: monitoring
spec:
selector:
matchLabels:
app: vmagent
template:
metadata:
labels:
app: vmagent
spec:
containers:
- name: vmagent
image: victoriametrics/vmagent:v1.102.0
args:
- "-promscrape.config=/config/scrape.yml"
- "-remoteWrite.url=https://metrics.example.com/api/v1/write"
- "-remoteWrite.tmpDataPath=/data/buffer"
- "-remoteWrite.maxDiskUsagePerURL=1GB"
volumeMounts:
- name: config
mountPath: /config
- name: buffer
mountPath: /data
volumes:
- name: config
configMap:
name: vmagent-config
- name: buffer
hostPath:
path: /var/lib/vmagent
type: DirectoryOrCreate
The maxDiskUsagePerURL setting is the safety net: when the buffer fills, vmagent drops the oldest data instead of crashing or filling the disk. On edge hardware, a full disk is a site outage.
Upgrading a thousand edge clusters is the hardest part of the job, and it is where most edge deployments fail.
# system-upgrade-controller plan for K3s
apiVersion: upgrade.cattle.io/v1
kind: Plan
metadata:
name: k3s-upgrade
namespace: system-upgrade
spec:
concurrency: 10
cordon: true
nodeSelector:
matchExpressions:
- key: node-role.kubernetes.io/control-plane
operator: In
values: ["true"]
serviceAccountName: system-upgrade
upgrade:
image: rancher/k3s-upgrade
version: v1.30.4-k3s1
The concurrency: 10 is the safety valve. Never upgrade the whole fleet at once.
Edge sites are physically accessible. A device in a retail back room can be unplugged, opened, or stolen. The security model has to assume the hardware is compromised.
latest at the edge.Kubernetes at the edge is not a smaller version of Kubernetes in the cloud. It is a different thing: a tool that has to survive conditions the cloud never faces, managed by people who are never on site, running on hardware that is old and small. The tools that work — K3s, Fleet, GitOps, immutable OS, lightweight observability — are the ones that accept those conditions instead of fighting them.
The cluster is a tool. Like all tools, it should withdraw into the work. At the edge, the work is keeping a thousand sites running while nobody watches. The art is building something that can do that, and then getting out of its way.