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

  1. Remove --docker and --container-runtime-endpoint from the node's flags/config so it uses embedded containerd, then restart k3s
  2. Or disable --embedded-registry on nodes that must keep using an external runtime
  3. 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

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


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/acde0ac83d6a5d1a. Report an issue: GitHub.