k3s vs MicroK8s vs Minikube: Which Lightweight Kubernetes Fits Your Use Case

Reading Time: 6 minutes

Kubernetes Ecosystem: From User to Contributor, Episode 3
← EP02: Minikube · EP03: k3s vs MicroK8s vs Minikube · EP04: Rancher →

10 min read


TL;DR

  • k3s vs MicroK8s vs Minikube comes down to one question first: do you need this to run in production on real hardware (k3s, MicroK8s), or only on a developer’s laptop (Minikube)?
  • k3s (built by Rancher, now part of SUSE) is a single ~70MB binary using SQLite or embedded etcd, designed explicitly for edge and IoT production deployments, not just local dev
  • MicroK8s (Canonical) is a snap-packaged cluster using Dqlite for HA — covered in EP01 — closer to k3s in intent than to Minikube
  • Minikube is the odd one out here: it’s VM/container-isolated and explicitly a local development tool, not something you’d run in production
  • Recommendation: for production edge/IoT, pick k3s or MicroK8s based on your packaging preference (binary vs snap) and datastore comfort (SQLite/etcd vs Dqlite); for local development and CI, pick Minikube when you need real isolation, or either k3s/MicroK8s when you just need “a cluster, fast”
  • Contribution opportunity: none of the three has a first-class way to migrate a running cluster’s workloads to another — a real, currently-unfilled gap

The Big Picture

                    k3s              MicroK8s           Minikube
                    ────             ────────           ────────
Packaging           Single binary    Snap package       VM/container
Intended for         Edge/IoT prod    Edge/IoT prod       Local dev only
Datastore (HA)       SQLite / etcd    Dqlite             etcd (per-node)
Multi-node HA        Yes              Yes                No (single profile
                                                           node, though multi-
                                                           profile exists)
Isolation from host   None (bare)      None (bare)        Full (VM or
                                                            container boundary)
Default footprint     ~70MB binary     ~200MB snap         500MB-1GB+ VM/image
Add-on model          Helm charts      snap add-ons        minikube addons
                      via manifests

k3s vs MicroK8s vs Minikube isn’t really a three-way tie — it’s two production-oriented, bare-metal tools (k3s, MicroK8s) and one deliberately isolated local-dev tool (Minikube) that happen to get compared because all three market themselves as “lightweight Kubernetes.”


Architecture at a Glance

k3s strips Kubernetes down to a single binary by removing in-tree cloud provider integrations, dropping alpha features, and swapping etcd for embedded SQLite by default (though it supports real etcd or external datastores for HA). It was purpose-built by Rancher Labs for resource-constrained edge devices and CI, and that heritage still defines its design decisions today.

MicroK8s — covered in full in EP01 — takes a different packaging route (a snap bundle rather than a single binary) but lands in almost the same use-case space: edge, IoT, and CI, with its own HA datastore (Dqlite) instead of etcd.

Minikube — covered in EP02 — is architecturally unlike either: it isolates the entire cluster inside a VM or container specifically so your laptop’s Kubernetes environment doesn’t interact directly with your laptop’s actual kernel and network stack. That isolation is a feature for local development and actively unwanted overhead for a production edge deployment.


Resource Footprint: What Each One Actually Costs to Run

# k3s — single binary, starts in seconds, minimal base memory
$ curl -sfL https://get.k3s.io | sh -
$ k3s kubectl get nodes
NAME       STATUS   ROLES                  AGE   VERSION
my-node    Ready    control-plane,master   12s   v1.28.5+k3s1

# MicroK8s — snap install, slightly heavier than k3s due to bundled containerd/Dqlite
$ sudo snap install microk8s --classic
$ microk8s status --wait-ready

# Minikube — heaviest by design, provisions a full VM or container first
$ minikube start --driver=docker
# (30-90 seconds depending on driver, before Kubernetes even starts booting)

On a resource-constrained edge device (a Raspberry Pi, an industrial gateway), the difference between k3s’s ~70MB binary and Minikube’s VM-based footprint isn’t a rounding error — it’s the difference between fitting on the device at all and not. This is why Minikube essentially never appears in edge deployment discussions: it was never built for that use case.


The Add-on / Component Model Compared

k3s MicroK8s Minikube
CNI Flannel (default), swappable Calico (default), swappable via add-on Varies by driver, addon-enabled
Ingress Traefik (bundled by default) nginx via add-on nginx via addon
Storage local-path-provisioner (bundled) hostpath-storage add-on default-storageclass addon
Extending Standard Helm charts, manifests microk8s enable <addon> minikube addons enable <name>

k3s ships more “batteries included” by default (Traefik and local-path storage are on unless you disable them) — a meaningfully different default posture from MicroK8s and Minikube, which both start closer to bare and expect you to opt in to what you need.


Recommendation: Which One Actually Fits Your Use Case

Running Kubernetes on real edge/IoT hardware in production: choose between k3s and MicroK8s based on packaging preference and datastore comfort, not raw features — they solve the same problem. If you’re already inside the snap ecosystem (Ubuntu Core, other Canonical tooling) or want a specific datastore, MicroK8s’s Dqlite. If you want the smallest possible footprint and the option of real etcd for HA, k3s. If you’re evaluating Rancher for fleet management (EP04), note that Rancher created k3s specifically to be its default downstream cluster type — that pairing has more operational precedent than any other combination here.

Local development, testing against something close to a real cloud node: Minikube, specifically when you need the VM isolation boundary — testing kernel-adjacent behavior, simulating a genuinely separate node, or needing multiple isolated profiles side by side.

CI pipelines needing a disposable cluster fast: k3s’s single-binary startup is hard to beat for raw speed; MicroK8s’s snap install is a close second. Minikube is the wrong tool here unless the CI environment specifically needs VM-level isolation for security reasons.

Don’t pick based on “most popular” or “newest” alone — all three are actively maintained, CNCF-conformant, and the “right” one is entirely determined by whether you’re targeting production hardware or a local workstation.


⚠ Production Gotchas

k3s’s default SQLite datastore is single-node only — HA requires explicit configuration. Don’t assume curl | sh gives you production HA out of the box; it gives you a working single node, and HA (embedded etcd or external datastore) is a deliberate follow-up step.

Comparing “footprint” numbers from marketing pages is misleading without matching workloads. A k3s binary’s on-disk size and MicroK8s’s snap size aren’t measuring the same thing (a binary vs. a bundle including containerd and a datastore) — benchmark actual running memory under your real workload, not install-time size.

None of these three are drop-in replacements for each other operationally, despite the “lightweight Kubernetes” label all three carry. Add-on names, default CNI, and default ingress all differ — migrating a manifest set between them is not guaranteed to work unmodified.


Quick Reference

# k3s
curl -sfL https://get.k3s.io | sh -
k3s kubectl get nodes
sudo systemctl status k3s

# MicroK8s
sudo snap install microk8s --classic
microk8s status --wait-ready
microk8s kubectl get nodes

# Minikube
minikube start --driver=<docker|kvm2|hyperkit|virtualbox>
minikube status
kubectl get nodes   # uses minikube's kubeconfig context directly

Contribution Opportunity: No First-Class Migration Path Between Them

The limitation: If you outgrow Minikube for local dev and want to mirror your production k3s environment more closely, or you’re running MicroK8s at the edge and want to evaluate switching to k3s, there’s no tooling in any of the three projects that translates the other’s add-on configuration, ingress setup, or storage class definitions into its own equivalent. You’re reproducing configuration by hand, from documentation, project by project.

Why it’s hard to fix: Each project’s add-on/component model evolved independently, solving the same category of problem (ingress, storage, networking) with different defaults and different configuration surfaces — there’s no shared standard to translate through, and no single maintainer group owns “compatibility between lightweight Kubernetes distros” as a problem, because each project’s maintainers are reasonably focused on their own users, not on easing exit to a competitor.

What a contribution-shaped fix looks like: A standalone, community-maintained translation tool or even a well-structured comparison-and-migration guide (living in a neutral location like a CNCF sandbox project or a widely-referenced GitHub repo, not owned by any one vendor) that maps common add-on configurations (ingress-nginx settings, storage class parameters, CNI policy syntax) between the three. This doesn’t require deep contribution to any single project’s core — it requires someone who has actually run workloads on more than one of these and is willing to document the translation precisely, which is exactly the kind of gap a practitioner (not a maintainer) is best positioned to fill.


Key Takeaways

  • k3s and MicroK8s are both production-oriented, bare-metal tools for edge/IoT; Minikube is a deliberately isolated local-dev tool — they’re not really three-way competitors on the same axis
  • k3s’s single-binary packaging and MicroK8s’s snap packaging solve the same problem differently — pick based on ecosystem fit and datastore preference, not raw capability
  • Minikube’s VM/container isolation is the right tool specifically when you need a real isolation boundary for local testing, not for general “I want Kubernetes on my laptop”
  • Default component choices differ meaningfully (Traefik vs nginx, bundled storage vs addon-based) — verify defaults before assuming any two of these behave the same out of the box
  • The most concrete, currently-unfilled contribution opportunity is configuration translation between the three — a documentation and tooling gap any experienced user could start closing

What’s Next

k3s was built by Rancher as the default cluster type for its own fleet-management platform. EP04 covers Rancher itself — what it actually does when you’re managing more than one cluster, and where its own control plane becomes another thing you have to operate.

Next: EP04 — Rancher: Multi-Cluster Kubernetes Management at Scale

Get EP04 in your inbox when it publishes → linuxcent.com/subscribe

MicroK8s Explained: The Single-Binary Kubernetes for Edge and IoT

Reading Time: 6 minutes

Kubernetes Ecosystem: From User to Contributor, Episode 1
← Kubernetes: From Borg to Platform Engineering · EP01: MicroK8s Explained · All Kubernetes Ecosystem Episodes →

11 min read


TL;DR

  • What is MicroK8s? A full, CNCF-conformant Kubernetes cluster packaged into a single snap package — install and have a running cluster in under 60 seconds, no separate container runtime or CNI install required
  • Built by Canonical for edge, IoT, and ephemeral CI clusters, where minimal footprint and single-command lifecycle matter more than fine-grained tuning
  • Add-ons (dns, storage, ingress, metallb, cilium, gpu) replace what you’d otherwise hand-install and wire together yourself
  • High-availability mode uses Dqlite (a distributed, Raft-backed SQLite) instead of etcd — a deliberate design choice with a real operational trade-off
  • The same “just works” install model that makes MicroK8s fast to stand up also hides some of the control-plane tuning knobs production operators expect from etcd
  • Contribution opportunity: Dqlite’s operational tooling is years behind etcd’s — a real, specific, achievable gap, covered in depth below

The Big Picture

TRADITIONAL KUBERNETES INSTALL              MICROK8S INSTALL
─────────────────────────────               ──────────────────
Install a container runtime                 $ sudo snap install microk8s --classic
Install kubelet, kubeadm, kubectl            $ microk8s status --wait-ready
Stand up and configure etcd
kubeadm init, join workers                          │
Install a CNI plugin                                ▼
Install an ingress controller                One snap install =
Install a storage provisioner                kubelet + kube-apiserver + kube-scheduler
Wire it all together, hope                   + kube-controller-manager + containerd
the versions are compatible                  + Dqlite + CNI (via add-on)
                                              — one running cluster, one command

What is MicroK8s? It’s Canonical’s single-binary distribution that packages an entire Kubernetes control plane and node into one snap package, so snap install microk8s produces a running, CNCF-conformant cluster without a separate container runtime, etcd install, or CNI setup step. The trade for that speed is control: the same bundling that removes a dozen manual steps also removes a dozen places you’d normally tune something.


How MicroK8s Fits an Entire Cluster Into One Snap

A snap package is a self-contained, confined Linux application bundle — MicroK8s ships its own containerd, its own CNI defaults, and its own datastore inside that bundle, rather than expecting the host to provide them.

$ sudo snap install microk8s --classic
microk8s (1.29/stable) v1.29.1 from Canonical✓ installed

$ microk8s status --wait-ready
microk8s is running
high-availability: no
  datastore master nodes: 127.0.0.1:19001
  datastore standby nodes: none
addons:
  enabled:
    ha-cluster            # always on — this is the Dqlite HA layer, even for a single node
  disabled:
    dns                   # ← you enable what you need, nothing runs by default beyond core
    ingress
    storage
    ...

--classic confinement is required because MicroK8s needs broader host access than a strictly-confined snap allows — it manages network interfaces, iptables/nftables rules, and mounts. This is the first place production operators coming from a bare-metal kubeadm install get surprised: MicroK8s’s snap confinement model means some host interactions happen through paths a traditional install never touches, and debugging network issues sometimes means understanding snap’s confinement boundaries, not just Kubernetes networking.


The Add-on Model: How MicroK8s Replaces a Day of Cluster Bootstrapping

Everything beyond the bare control plane is an add-on, enabled with one command:

$ microk8s enable dns storage ingress
Infer repository core for addon dns
Enabling DNS
...
DNS is enabled
Infer repository core for addon storage
Enabling default storage class
...
Storage is enabled
Infer repository core for addon ingress
Ingress controller is enabled

$ microk8s kubectl get pods -A
NAMESPACE     NAME                                      READY   STATUS    RESTARTS
kube-system   coredns-864597b5fd-x7k2p                  1/1     Running   0
kube-system   hostpath-provisioner-5c65c9c74f-j9qmz      1/1     Running   0
ingress       nginx-ingress-microk8s-controller-abcde    1/1     Running   0

Each add-on is a maintained, version-pinned bundle — enabling ingress doesn’t pull the latest ingress-nginx release, it pulls whatever version that MicroK8s release channel has validated. That’s the same trade seen everywhere else in this tool: predictability and speed, at the cost of being slightly behind upstream and unable to mix-and-match component versions the way a hand-built cluster can.

The cilium add-on is worth calling out specifically for platform teams already standardizing on eBPF-based networking elsewhere — it replaces MicroK8s’s default CNI with Cilium, giving you the same eBPF-enforced network policy model covered in the TC eBPF episode of the eBPF series, without a separate Cilium install process.


Dqlite Instead of etcd: MicroK8s’s Most Debated Design Choice

For high availability, upstream Kubernetes distributions almost universally reach for etcd — a mature, Raft-based key-value store with over a decade of production hardening. MicroK8s uses Dqlite instead: a distributed SQLite built by Canonical, also Raft-based, but SQL-native rather than a plain key-value store.

# Convert a single node into a 3-node HA cluster
$ microk8s add-node
From the target node, run:
microk8s join 10.0.1.15:25000/abc123... --worker

$ microk8s status
microk8s is running
high-availability: yes
  datastore master nodes: 10.0.1.10:19001 10.0.1.12:19001 10.0.1.15:19001
  datastore standby nodes: none

The reasoning behind Dqlite is architectural: Canonical wanted a datastore that could also serve their other projects (LXD, for instance) with a SQL interface, not just Kubernetes’ key-value needs, and one they could tightly control the release cadence of rather than depending on the etcd project’s own timeline. That’s a legitimate engineering decision — but it means MicroK8s’s HA story runs on a datastore with a small fraction of etcd’s operational track record.


Where MicroK8s Actually Runs in Production

MicroK8s’s real fit is narrower than “any Kubernetes workload”: edge and IoT deployments where a device needs a full, conformant cluster with no external dependencies (a factory sensor gateway, a retail point-of-sale cluster); CI/CD pipelines that need a disposable, fast-booting cluster per test run; and single-node developer or demo environments where Minikube’s VM overhead isn’t wanted.

It’s a weaker fit for large multi-tenant production clusters where teams already have deep etcd operational expertise, need fine-grained control-plane component versioning, or run at a scale where Dqlite’s newer, less-battle-tested Raft implementation is a harder sell to a risk-averse platform team.


⚠ Production Gotchas

Dqlite HA needs an odd number of nodes, same as etcd — but the community knowledge base is much thinner. A 2-node or 4-node Dqlite cluster has the identical split-brain risk etcd has at even node counts. The difference is that when something goes wrong, etcd has ten years of Stack Overflow answers and postmortems; Dqlite has a fraction of that.

microk8s kubectl and a separately-installed kubectl are not automatically the same context. Running both on one host is a common source of “why isn’t my change showing up” confusion — always check which kubeconfig each one is actually pointed at.

Add-on versions lag upstream by design. If a CVE fix or a new feature lands in upstream ingress-nginx or Cilium, MicroK8s’s bundled add-on version won’t have it until the next MicroK8s release validates it. Don’t assume microk8s enable X gets you the latest X.

Classic confinement means MicroK8s can conflict with other host-level network tooling. Firewalls, VPN clients, or other snap-confined networking tools on the same host can produce iptables/nftables rule conflicts that look like a Kubernetes networking bug but are actually a confinement-boundary interaction.


Quick Reference

microk8s status --wait-ready       # cluster health, HA state, enabled add-ons
microk8s enable <addon>            # dns, storage, ingress, metallb, cilium, gpu, ...
microk8s disable <addon>           # remove an add-on
microk8s kubectl <args>            # bundled kubectl, uses MicroK8s's own kubeconfig
microk8s add-node                  # generate a join token for HA/worker expansion
microk8s join <token>               # join a node using that token
microk8s inspect                   # generate a full diagnostic tarball for support/debugging
microk8s remove-node <node>         # remove a node from an HA cluster

Contribution Opportunity: Dqlite’s Missing Decade of Tooling

The limitation: etcd operators have etcdctl endpoint status, etcdctl endpoint health, mature snapshot/restore tooling, and a decade of documented failure-mode runbooks. Dqlite’s CLI surface for diagnosing a struggling Raft cluster — checking leader state, log index lag between nodes, or safely restoring from a snapshot after a prolonged partition — is meaningfully thinner. When a MicroK8s HA node fails to rejoin cleanly, there’s far less prior art to lean on than for the etcd equivalent.

Why it’s hard to fix: This isn’t a bug sitting in an issue tracker waiting for a quick patch. It’s a maturity gap that comes from Dqlite being a newer, narrower-scope project (built primarily to serve Canonical’s own products) maintained by a much smaller team than the decade of enterprise-scale usage that produced etcd’s tooling. Closing that gap is a sustained, multi-quarter documentation-and-tooling effort, not a single PR — and it competes for the Dqlite maintainers’ time against Canonical’s own product roadmap, which doesn’t automatically prioritize the broader Kubernetes community’s operational wishlist.

What a contribution-shaped fix looks like: Two concrete, achievable starting points that don’t require deep Raft-internals expertise: (1) a dqlite-side diagnostic command mirroring etcdctl endpoint status — human-readable leader/term/log-index output — contributed to canonical/go-dqlite; or (2) reproducing and documenting specific HA failure scenarios (node rejoin after a prolonged network partition, recovery from a minority-node failure) as runbooks in canonical/microk8s‘s own documentation repo, with the exact recovery commands verified against a real reproduction. The second option in particular is the kind of contribution an engineer who’s actually operated MicroK8s in production is uniquely positioned to make — it needs careful reproduction and clear writing, not kernel-level systems expertise.


Key Takeaways

  • MicroK8s packages a full, CNCF-conformant Kubernetes cluster into a single snap install — no separate runtime, etcd, or CNI setup required
  • The add-on model trades version flexibility for predictability: what you enable is validated and bundled, not necessarily the latest upstream release
  • Dqlite replaces etcd for HA — a legitimate architectural choice, but one with far less operational tooling and community track record behind it
  • MicroK8s’s real fit is edge/IoT, ephemeral CI clusters, and single-node dev environments — not large multi-tenant production clusters with deep etcd expertise already in place
  • The clearest contribution opportunity here doesn’t require Raft internals — it requires operating MicroK8s in production long enough to hit a real failure mode and documenting it precisely

What’s Next

MicroK8s trades control for a near-instant single-command cluster. EP02 looks at Minikube — the other dominant “local Kubernetes” tool, built on a different trade-off entirely: a full VM (or container-based driver) per cluster instead of a bare-metal snap install, and where that heavier model actually earns its overhead.

Next: EP02 — Minikube: Local Kubernetes Done Right — and Where It Breaks Down

Get EP02 in your inbox when it publishes → linuxcent.com/subscribe