k3s-io/k3s · error
embedded registry mirror requires embedded containerd
Error message
embedded registry mirror requires embedded containerd
What it means
The embedded registry mirror (spegel) requires pods to pull through k3s's own containerd so it can intercept and serve pulls. If the node is configured to use an external runtime (--docker or --container-runtime-endpoint), k3s cannot hook image pulls, so enabling --embedded-registry together with either flag fails fast during agent startup.
Source
Thrown at pkg/agent/run.go:119
}
// The net/bridge/bridge-nf-call-{ip,ip6}tables sysctls are only required by kube-proxy
// and flannel. When both are disabled the node is using an alternative CNI, so leave
// these sysctls untouched for the administrator to manage. See
// https://github.com/k3s-io/k3s/issues/14022. The flannel backend is compared against the
// "none" literal rather than flannel.BackendNone to avoid importing the flannel package,
// which registers all flannel backends via init().
setBridgeFilter := !config.KubeProxyDisabled(ctx, nodeConfig, proxy) || nodeConfig.Flannel.Backend != "none"
syssetup.Configure(enableIPv6, setBridgeFilter, conntrackConfig)
nodeConfig.AgentConfig.EnableIPv4 = enableIPv4
nodeConfig.AgentConfig.EnableIPv6 = enableIPv6
if err := executor.Bootstrap(ctx, nodeConfig, cfg); err != nil {
return err
}
if nodeConfig.EmbeddedRegistry {
if nodeConfig.Docker || nodeConfig.ContainerRuntimeEndpoint != "" {
return errors.New("embedded registry mirror requires embedded containerd")
}
if err := spegel.DefaultRegistry.Start(ctx, nodeConfig, executor.CRIReadyChan()); err != nil {
return errors.WithMessage(err, "failed to start embedded registry")
}
}
if nodeConfig.SupervisorMetrics {
if err := metrics.DefaultMetrics.Start(ctx, nodeConfig); err != nil {
return errors.WithMessage(err, "failed to serve metrics")
}
}
if nodeConfig.EnablePProf {
if err := profile.DefaultProfiler.Start(ctx, nodeConfig); err != nil {
return errors.WithMessage(err, "failed to serve pprof")
}
}View on GitHub (pinned to 6ba341e396)
Solutions
- Remove --docker and --container-runtime-endpoint from the node's flags/config so it uses embedded containerd, then restart k3s
- Or disable --embedded-registry on nodes that must keep using an external runtime
- Scan /etc/rancher/k3s/config.yaml and systemd unit drop-ins for leftover runtime flags after config merges
Example fix
# before k3s agent --docker --embedded-registry ... # -> embedded registry mirror requires embedded containerd # after k3s agent --embedded-registry ... # default embedded containerd
Defensive patterns
Strategy: validation
Validate before calling
if nodeConfig.EmbeddedRegistry && (nodeConfig.Docker || nodeConfig.ContainerRuntimeEndpoint != "") {
return errors.New("--embedded-registry is incompatible with --docker / --container-runtime-endpoint")
} Prevention
- Validate flag combinations in config management (Ansible/Helm values) before rollout
- When enabling the embedded registry, remove leftover docker/runtime-endpoint keys from config.yaml
- Document that spegel requires the embedded containerd socket
When it happens
Trigger: Passing --embedded-registry on a node that also has --docker or --container-runtime-endpoint set (via CLI flags or /etc/rancher/k3s/config.yaml).
Common situations: Teams enabling the embedded registry on existing Docker-based nodes; config.yaml retaining old --docker entries after adding the new registry flag; GitOps templates merging both options.
Related errors
- insufficient PSK bytes
- invalid endpoint URL %s for %s: %v
- default runtime %s was not found
- nix-store not found in PATH: install nix (https://nixos.org/
- cri-dockerd disabled at build time
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/acde0ac83d6a5d1a.
Report an issue: GitHub.