Save products you love by clicking the heart icon.
Evidence-based testing practice from production codebases — property-based invariants, seeded fault injection, contract tests, benchmark-verified performance, and quality gates that are enforced, not aspirational.
A comprehensive comparison of k3s distributions (official, k3sup, k3d, AutoK3s) and CNI plugins (Flannel, Calico, Cilium, Canal) covering NetworkPolicy support, performance benchmarks, ingress, storage, and production deployment considerations.
k3s is optimized for edge computing scenarios where you need to deploy and manage hundreds or thousands of small Kubernetes clusters across distributed sites. This article explores proven patterns for multi-cluster management, offline-first deployments, and observability at scale using k3s.
Managing distributed edge clusters requires hierarchical patterns, GitOps workflows, and centralized control planes that can scale to thousands of clusters.
Rancher Fleet is a GitOps multi-cluster management tool designed for Kubernetes at scale. It follows a hierarchical control plane model:
Central Cluster (Rancher + Fleet)
↓ GitOps repositories
↓
Regional Clusters (Hub clusters)
↓ Fleet agents
↓
Edge Clusters (Leaf nodes)
Key capabilities:
Fleet CLI basics:
# Register a downstream cluster
fleetctl login https://fleet.example.com
fleetctl cluster register --name store-001 --labels region=us-west,zone=store
# Create a GitRepo resource
fleetctl gitrepo create \
--name my-app \
--repo https://github.com/org/manifests \
--paths ./clusters/production \
--targets "clusterSelectors.matchLabels.region=us-west"
# Trigger rollout
fleetctl gitrepo update my-app --force
GitOps is essential for edge deployments because it provides:
GitRepo resource example:
apiVersion: fleet.cattle.io/v1alpha1
kind: GitRepo
metadata:
name: retail-app
namespace: fleet-default
spec:
repo: https://github.com/retail/edge-manifests
branch: main
paths:
- ./clusters/production
targets:
- clusterSelector:
matchLabels:
region: us-west
site-type: retail-store
revisionHistoryLimit: 10
For distributed edge sites, use a 3-tier hierarchy:
| Tier | Purpose | Hardware | Connectivity | Management |
|---|---|---|---|---|
| Central | Control plane, dashboards, repositories | Multiple servers, HA | Reliable broadband | Full Rancher UI |
| Regional | Aggregate state, caching, failover | Mid-range servers | Fiber or LTE | Regional IT staff |
| Edge | Workload execution | Mini PCs, industrial hardware | Intermittent (LTE/5G, fiber) | Fleet agents only |
Regional cluster benefits:
A national retail chain deployed k3s to 500+ stores using Rancher Fleet:
Challenges:
Solution:
Central Rancher (k3s)
↓ GitOps manifests
↓ Fleet agents
↓
500+ Store k3s clusters (Raspberry Pi 4 + industrial chassis)
Results:
Fleet manifest for retail stores:
apiVersion: fleet.cattle.io/v1alpha1
kind: GitRepo
metadata:
name: retail-store
spec:
repo: https://github.com/retail/store-manifests
paths:
- ./clusters/production
targets:
- clusterSelector:
matchLabels:
environment: production
store-type: retail
helm:
releaseName: pos-system
chart: ./charts/pos-system
valuesFiles:
- ./clusters/production/values/store-default.yaml
Edge sites often have unreliable or no connectivity. Offline-first patterns ensure clusters can bootstrap, update, and operate without central access.
k3s supports airgap installation via a bundled images archive:
Step 1: Download airgap bundle on connected machine
# Download k3s binary and airgap images
wget https://github.com/k3s-io/k3s/releases/download/v1.30.2/k3s-airgap-images-amd64.tar.gz
wget https://github.com/k3s-io/k3s/releases/download/v1.30.2/k3s
# Copy to airgap media
cp k3s k3s-airgap-images-amd64.tar.gz /media/usb/
Step 2: Transfer to airgap machine and install
# Import images
sudo mkdir -p /var/lib/rancher/k3s/agent/images/
sudo cp /media/usb/k3s-airgap-images-amd64.tar.gz /var/lib/rancher/k3s/agent/images/
sudo chmod 644 /var/lib/rancher/k3s/agent/images/*.tar.gz
# Install k3s
sudo install /media/usb/k3s /usr/local/bin/
# Start k3s
sudo INSTALL_K3S_SKIP_DOWNLOAD=true \
INSTALL_K3S_SKIP_ENABLE=true \
INSTALL_K3S_EXEC="server --no-deploy traefik" \
/usr/local/bin/k3s
Step 3: Verify airgap installation
# Check images are imported
sudo crictl images | grep -E "rancher|pause"
# Verify cluster
kubectl get nodes
Deploy a private container registry at each edge site to cache images:
Harbor deployment via Helm:
# harbor-values.yaml
expose:
type: nodePort
tls:
enabled: false
persistence:
enabled: true
resourcePolicy: "keep"
persistentVolumeClaim:
registry:
storageClass: "local-path"
portal:
replicas: 1
registry:
replicas: 1
storage:
type: filesystem
# Install Harbor
helm repo add harbor https://helm.goharbor.io
helm install harbor harbor/harbor -f harbor-values.yaml -n harbor --create-namespace
# Push images to local registry
docker tag rancher/local-path-provisioner:v0.0.26 harbor.local/rancher/local-path-provisioner:v0.0.26
docker push harbor.local/rancher/local-path-provisioner:v0.0.26
Configure k3s to use local registry:
# Create registry mirror config
sudo mkdir -p /etc/rancher/k3s
cat <<EOF | sudo tee /etc/rancher/k3s/registries.yaml
mirrors:
docker.io:
endpoint:
- http://harbor.local/v2/registry/mirrors/docker.io
registry.k8s.io:
endpoint:
- http://harbor.local/v2/registry/mirrors/registry.k8s.io
EOF
# Restart k3s
sudo systemctl restart k3s
Spegel is a P2P registry mirror that runs on each k3s node:
# spegel-helm-values.yaml
image:
repository: ghcr.io/xenitab/spegel
tag: v0.0.18
registry:
mirror:
enabled: true
interval: 10m
registries:
- https://registry.k8s.io
- https://docker.io
podMonitor:
enabled: true
# Install Spegel
helm repo add spegel https://charts.spegel.io
helm install spegel spegel/spegel -f spegel-helm-values.yaml -n kube-system
How Spegel works:
Benefits:
Observability across thousands of edge clusters requires bandwidth-efficient patterns, hierarchical aggregation, and resilient telemetry collection.
Standard monitoring stack adapted for edge:
# prometheus-operator-values.yaml
prometheus:
prometheusSpec:
retention: 15d
storageSpec:
volumeClaimTemplate:
spec:
storageClassName: local-path
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 500m
memory: 1Gi
nodeExporter:
enabled: true
grafana:
enabled: true
persistence:
enabled: true
size: 5Gi
storageClassName: local-path
Edge Prometheus configurations:
Loki provides cost-effective log aggregation for edge:
# loki-stack-values.yaml
loki:
persistence:
enabled: true
storageClassName: local-path
size: 10Gi
config:
limits_config:
retention_period: 168h # 7 days
ingester:
lifecycler:
ring:
kvstore:
store: inmemory
fluent-bit:
enabled: true
config:
service: |
[SERVICE]
Flush 1
Daemon Off
Log_Level info
Parsers_File parsers.conf
inputs: |
[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
Tag kube.*
Mem_Buf_Limit 5MB
Skip_Long_Lines On
filters: |
[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Kube_Tag_Prefix kube.var.log.containers.
Merge_Log On
Merge_Log_Key log_processed
K8S-Logging.Parser On
K8S-Logging.Exclude On
outputs: |
[OUTPUT]
Name loki
Match *
Host loki.monitoring.svc
Port 3100
Labels job=fluent-bit,cluster=store-001,region=us-west
Line_Format json
Loki optimization for edge:
For long-term metrics retention and cross-cluster analytics:
# thanos-values.yaml
query:
replicas: 1
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
queryFrontend:
replicas: 1
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
store:
replicas: 1
persistence:
storageClass: local-path
size: 50Gi
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 200m
memory: 512Mi
objstore:
type: s3
config:
endpoint: minio.storage.svc.cluster.local
bucket: thanos
access_key: ${THANOS_S3_ACCESS_KEY}
secret_key: ${THANOS_S3_SECRET_KEY}
Thanos remote_write from edge Prometheus:
# remote_write configuration
remote_write:
- url: http://thanos-receive.thanos.svc.cluster.local:19291/api/v1/receive
queue_config:
capacity: 10000
max_shards: 10
min_shards: 1
max_samples_per_send: 1000
batch_send_deadline: 5s
min_backoff: 30ms
max_backoff: 100ms
retry_on_http_429: true
write_relabel_configs:
- source_labels: [__name__]
regex: "go_.*|process_.*"
action: drop # Drop low-value metrics
Tail-based sampling (Loki):
# Fluent Bit filter for sampling
[FILTER]
Name sample
Match kube.*
Rate 10 # Keep 10% of logs
Sample_Key level
Sample_Values info,warn,error,critical
Metrics aggregation (Prometheus):
# recording rules for edge aggregation
groups:
- name: edge_aggregation
interval: 30s
rules:
- record: cluster:node_cpu_usage:avg5m
expr: avg(rate(node_cpu_seconds_total[5m])) by (cluster)
- record: cluster:node_memory_usage:avg5m
expr: avg(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) by (cluster)
- record: cluster:container_memory_usage_bytes:sum
expr: sum(container_memory_working_set_bytes) by (cluster, namespace)
Downsampling for long-term storage:
# Thanos downsampling configuration
downsample:
enable: true
retention:
raw: 30d
five_minutes: 90d
one_hour: 1y
Use bbolt for reliable telemetry during outages:
[OUTPUT]
Name loki
Match *
Host loki.monitoring.svc
Port 3100
Labels job=fluent-bit,cluster=store-001
storage_path /var/lib/fluent-bit
storage_sync normal
storage_checksum off
storage_max_backups 3
storage_max_chunks_up 128
storage_chunk_size 1M
How bbolt works:
Design network topology for edge resilience:
Internet
|
↓
[Central Data Center]
| |
↓ (fiber/VPN) ↓ (LTE/5G backup)
[Regional Hub A] [Regional Hub B]
| |
↓ (local fiber) ↓ (local fiber)
[Edge Stores] [Edge Stores]
(mesh P2P via Spegel) (mesh P2P via Spegel)
Best practices:
Recommended hardware for k3s edge nodes:
| Cluster Size | CPU | RAM | Storage | Network |
|---|---|---|---|---|
| Small (1-5 pods) | 2 cores | 4GB | 50GB SSD | 100 Mbps |
| Medium (5-20 pods) | 4 cores | 8GB | 100GB SSD | 1 Gbps |
| Large (20-50 pods) | 8 cores | 16GB | 250GB SSD | 1 Gbps |
k3s resource tuning:
# k3s config for edge
controller-args:
- --disable=traefik
- --disable=servicelb
- --disable=metrics-server
- --kubelet-arg=pod-max-pods=50
- --kubelet-arg=cgroups-per-qos=true
- --kubelet-arg=system-reserved=cpu=200m,memory=512Mi
- --kubelet-arg=kube-reserved=cpu=200m,memory=512Mi
Security hardening for edge deployments:
Admission controller for image scanning:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: trivy-admission-controller
webhooks:
- name: trivy.kb.io
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
clientConfig:
service:
name: trivy
namespace: trivy-system
admissionReviewVersions: ["v1"]
sideEffects: None
| Scenario | Pattern | Tools |
|---|---|---|
| 10-100 clusters, connected | Simple Fleet | Fleet + k3s + GitHub |
| 100-1000 clusters, some offline | Fleet + Harbor | Fleet + Harbor + Airgap |
| 1000+ clusters, distributed | Hierarchical Fleet | Fleet + Regional hubs + Spegel |
| Bandwidth-constrained | P2P registry | Spegel + Local-path storage |
| Long-term analytics | Thanos/Cortex | Thanos + MinIO + Downsampling |
| Offline-first | Full airgap | Airgap images + Harbor + bbolt |
k3s provides a solid foundation for edge computing scenarios, but successful deployments require patterns for:
The combination of Fleet's GitOps control plane, Harbor's registry caching, and Spegel's P2P distribution creates a resilient edge computing platform that scales from tens to thousands of clusters while maintaining operational simplicity and offline resilience.