cilium/cilium · error

failed to set MTU %d for %s: %w

Error message

failed to set MTU %d for %s: %w

What it means

setupBaseDevice sets the configured MTU on cilium_host with netlink.LinkSetMTU. When the kernel rejects the MTU change on the host device, this wrapped error is returned with the MTU value and device name. It means datapath initialization aborts because the interface MTU could not be applied.

Source

Thrown at pkg/datapath/loader/netlink.go:148

// the first step of datapath initialization, then performs the setup (and
// creation, if needed) of those interfaces. It returns two links and an error.
// By default, it sets up the veth pair - cilium_host and cilium_net.
func setupBaseDevice(logger *slog.Logger, sysctl sysctl.Sysctl, mtu int) (netlink.Link, netlink.Link, error) {
	if err := setupVethPair(logger, sysctl, defaults.HostDevice, defaults.SecondHostDevice); err != nil {
		return nil, nil, fmt.Errorf("failed to setup veth pair: %w", err)
	}

	linkHost, err := safenetlink.LinkByName(defaults.HostDevice)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to get link for %s: %w", defaults.HostDevice, err)
	}
	linkNet, err := safenetlink.LinkByName(defaults.SecondHostDevice)
	if err != nil {
		return nil, nil, fmt.Errorf("failed to get link for %s: %w", defaults.SecondHostDevice, err)
	}

	if err := netlink.LinkSetMTU(linkHost, mtu); err != nil {
		return nil, nil, fmt.Errorf("failed to set MTU %d for %s: %w", mtu, linkHost.Attrs().Name, err)
	}
	if err := netlink.LinkSetMTU(linkNet, mtu); err != nil {
		return nil, nil, fmt.Errorf("failed to set MTU %d for %s: %w", mtu, linkNet.Attrs().Name, err)
	}

	return linkHost, linkNet, nil
}

// addHostDeviceAddr add internal ipv4 and ipv6 addresses to the cilium_host device.
func addHostDeviceAddr(hostDev netlink.Link, ipv4, ipv6 net.IP) error {
	if ipv4 != nil {
		addr := netlink.Addr{
			IPNet: &net.IPNet{
				IP:   ipv4,
				Mask: net.CIDRMask(32, 32), // corresponds to /32
			},
		}

View on GitHub (pinned to ac7b90affa)

Solutions

  1. Check the configured MTU option and set a valid value (typically 1500, or physical MTU minus encapsulation overhead).
  2. Inspect the wrapped kernel error: EINVAL means out-of-range MTU; ENODEV means the link vanished — restart to recreate the pair.
  3. When using Geneve/VXLAN with big TCP or custom MTU, compute overhead correctly (e.g. 1500 - 50 for Geneve over IPv4).
  4. Ensure nothing concurrently deletes cilium_host during startup.

Example fix

// before
cilium-agent --mtu 9000   # underlying NIC MTU is 1500
// after
cilium-agent --mtu 1500   # or 1450 for VXLAN/GENEVE overhead over a 1500 NIC
Defensive patterns

Strategy: validation

Validate before calling

// validate MTU before calling into the loader
physMTU, _ := getNicMTU(primaryDevice)
maxMTU := physMTU - tunnelOverhead(mode) // e.g. 50 for Geneve
if mtu <= 0 || mtu > maxMTU {
    return fmt.Errorf("configured MTU %d out of range (max %d)", mtu, maxMTU)
}

Try / catch

if err := netlink.LinkSetMTU(linkHost, mtu); err != nil {
    if errors.Is(err, unix.EINVAL) {
        return fmt.Errorf("MTU %d invalid for %s; check device limits", mtu, linkHost.Attrs().Name)
    }
    return err
}

Prevention

When it happens

Trigger: netlink.LinkSetMTU(linkHost, mtu) fails on cilium_host — typically EINVAL for an MTU outside the device's allowed range, or the device disappeared mid-setup.

Common situations: Misconfigured --agent-not-ready-tolerate- MTU / tunnel MTU values (e.g. 0 or huge values like 65535) exceeding what veth/cilium_host accepts; encapsulation overhead making the requested MTU larger than the underlying NIC supports; race where the device was deleted between lookup and set.

Related errors


AI-assisted analysis of cilium/cilium@ac7b90affa (2026-08-31). Data as JSON: /api/errors/8341d59a273cb187. Report an issue: GitHub.