Cilium CNI can be installed on Talos Linux using several methods, ranging from a fully declarative GitOps-friendly approach to a quick one-command install for development clusters.
The right method depends on whether you’re using Omni, what your production requirements are, and how much you want the installation to persist across reboots.
Each method supports both the default kube-proxy mode and kube-proxy-free operation, covered by tabs in each section.
| Method | Recommended for | Notes |
|---|
| Omni manifest sync | Omni users | Recommended path for Omni clusters |
| Helm | All other Talos clusters | Four variants — inline, apply, hosted, CLI; choose based on your workflow |
| Cilium CLI | Development / testing | Quickest path, least declarative |
Prerequisites
Talos Linux has specific requirements that apply regardless of which installation method you choose:
- IPAM mode must be set to
kubernetes
- Talos already provides
cgroupv2 and bpffs mounts — do not let Cilium attempt to mount these
- Talos does not allow Kubernetes workloads to load kernel modules, so the
SYS_MODULE capability must be dropped from Cilium’s default capability set
- This guide assumes KubePrism is enabled and configured on port
7445
Machine configuration prerequisites
In addition to the above prerequisites, the Cilium CLI and Helm methods all require that you first configure your machine with CNI set to none. Set this before bootstrapping.
To do that:
With kube-proxy
Without kube-proxy
1. Create a patch file that sets the CNI to none:cat <<EOF > patch.yaml
cluster:
network:
cni:
name: none
EOF
2. Generate the machine config with the patch applied:talosctl gen config \
my-cluster https://mycluster.local:6443 \
--config-patch @patch.yaml
1. Create a patch file that sets the CNI to none and disables kube-proxy:cat <<EOF > patch.yaml
cluster:
network:
cni:
name: none
proxy:
disabled: true
EOF
2. Generate the machine config with the patch applied:talosctl gen config \
my-cluster https://mycluster.local:6443 \
--config-patch @patch.yaml
After applying the machine config with CNI set to none and bootstrapping, Talos will appear to hang at phase 18/19 with the message retrying error: node not ready. This is expected, nodes are only marked ready once a CNI is running. You have approximately 10 minutes to apply Cilium before the node reboots to retry.
Method 1: Omni manifest sync
If you are using Omni, the recommended approach is to deploy Cilium using the manifest sync feature in a cluster template. Omni will wait until the Kubernetes API is available and the cluster is healthy before applying the Cilium manifests.
Step 1. Create a values.yaml file with the Cilium Helm values by running:
With kube-proxy
Without kube-proxy
cat <<EOF > values.yaml
ipam:
mode: kubernetes
kubeProxyReplacement: false
securityContext:
capabilities:
ciliumAgent:
- CHOWN
- KILL
- NET_ADMIN
- NET_RAW
- IPC_LOCK
- SYS_ADMIN
- SYS_RESOURCE
- DAC_OVERRIDE
- FOWNER
- SETGID
- SETUID
cleanCiliumState:
- NET_ADMIN
- SYS_ADMIN
- SYS_RESOURCE
cgroup:
autoMount:
enabled: false
hostRoot: /sys/fs/cgroup
EOF
cat <<EOF > values.yaml
ipam:
mode: kubernetes
kubeProxyReplacement: true
k8sServiceHost: localhost
k8sServicePort: 7445
securityContext:
capabilities:
ciliumAgent:
- CHOWN
- KILL
- NET_ADMIN
- NET_RAW
- IPC_LOCK
- SYS_ADMIN
- SYS_RESOURCE
- DAC_OVERRIDE
- FOWNER
- SETGID
- SETUID
cleanCiliumState:
- NET_ADMIN
- SYS_ADMIN
- SYS_RESOURCE
cgroup:
autoMount:
enabled: false
hostRoot: /sys/fs/cgroup
EOF
Step 2. Render the Cilium manifests using Helm:
helm repo add cilium https://helm.cilium.io/
helm repo update
helm template \
cilium \
cilium/cilium \
--version 1.18.0 \
--namespace kube-system \
--values values.yaml > cilium.yaml
Step 3. Reference the rendered manifest in your Omni cluster template using the file field:
When deploying without kube-proxy, set proxy.disabled: true in the patches block:
Step 4. Apply the cluster template:
omnictl cluster template sync --file cluster-template.yaml
See Sync Kubernetes Manifests for more on manifest sync modes and status monitoring.
Method 2: Helm
Helm is the recommended approach for Talos clusters not managed by Omni. There are four variants depending on how you want to manage and apply the Cilium manifest. Choose the one that fits your workflow:
- CLI install: Installs Cilium directly with
helm install without generating a manifest file. Simplest Helm path but least declarative.
- Inline manifest: Embeds the manifest directly in the machine configuration; applied automatically on bootstrap and reapplied on control plane reboots. Recommended for production.
- Manifest apply: Generates the manifest with
helm template and applies it manually with kubectl. Useful for one-off installs or when you want to inspect the manifest first.
- Hosted manifest: Generates the manifest, hosts it at a URL, and patches the machine config to fetch it automatically during bootstrap. Requires secure internal hosting.
CLI install
Inline manifest
Manifest apply
Hosted manifest
Install Cilium directly using helm install during the bootstrap window.helm repo add cilium https://helm.cilium.io/
helm repo update
helm install \
cilium \
cilium/cilium \
--version 1.18.0 \
--namespace kube-system \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=false \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup
helm repo add cilium https://helm.cilium.io/
helm repo update
helm install \
cilium \
cilium/cilium \
--version 1.18.0 \
--namespace kube-system \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=true \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup \
--set k8sServiceHost=localhost \
--set k8sServicePort=7445
Install Gateway API CRDs first, then:helm repo add cilium https://helm.cilium.io/
helm repo update
helm install \
cilium \
cilium/cilium \
--version 1.18.0 \
--namespace kube-system \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=true \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup \
--set k8sServiceHost=localhost \
--set k8sServicePort=7445 \
--set gatewayAPI.enabled=true \
--set gatewayAPI.enableAlpn=true \
--set gatewayAPI.enableAppProtocol=true
After Cilium is installed successfully, the bootstrap process will continue and complete. The Cilium manifest is embedded directly in the machine configuration and applied automatically during bootstrap.
Before you start:
- Only add the Cilium inline manifest to control plane node machine configurations, and make sure all control plane nodes have an identical configuration.
- Changing the namespace when templating with Helm does not generate a manifest that creates that namespace. If you change the namespace, manually prepend the namespace YAML at the top of the inline manifest.
- Talos only creates missing resources from inline manifests — it never deletes or updates them. To update the manifest, edit all control plane machine configurations and run
talosctl upgrade-k8s.
Step 1. Generate the Cilium manifest: With kube-proxy
Without kube-proxy
helm repo add cilium https://helm.cilium.io/
helm repo update
helm template \
cilium \
cilium/cilium \
--version 1.18.0 \
--namespace kube-system \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=false \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup > cilium.yaml
helm repo add cilium https://helm.cilium.io/
helm repo update
helm template \
cilium \
cilium/cilium \
--version 1.18.0 \
--namespace kube-system \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=true \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup \
--set k8sServiceHost=localhost \
--set k8sServicePort=7445 > cilium.yaml
Step 2. Embed the generated manifest in your machine configuration:cluster:
inlineManifests:
- name: cilium
contents: |
---
# Source: cilium/templates/cilium-agent/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: "cilium"
namespace: kube-system
---
# Paste the full contents of cilium.yaml here
Generate the manifest with helm template and apply it manually with kubectl during the bootstrap window. With kube-proxy
Without kube-proxy
helm repo add cilium https://helm.cilium.io/
helm repo update
helm template \
cilium \
cilium/cilium \
--version 1.18.0 \
--namespace kube-system \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=false \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup > cilium.yaml
kubectl apply -f cilium.yaml
helm repo add cilium https://helm.cilium.io/
helm repo update
helm template \
cilium \
cilium/cilium \
--version 1.18.0 \
--namespace kube-system \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=true \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup \
--set k8sServiceHost=localhost \
--set k8sServicePort=7445 > cilium.yaml
kubectl apply -f cilium.yaml
Generate the manifest, host it at a secure internal URL, and patch the machine config to fetch it automatically during bootstrap.The Helm-generated Cilium manifest contains sensitive key material. Do not host it somewhere publicly accessible.
Step 1. Generate cilium.yaml using helm template as shown in the Manifest apply tab above.Step 2. Host the file at a secure internal URL, then patch your machine configuration:cat <<EOF > patch.yaml
cluster:
network:
cni:
name: custom
urls:
- https://server.yourdomain.tld/some/path/cilium.yaml
EOF
talosctl gen config \
my-cluster https://mycluster.local:6443 \
--config-patch @patch.yaml
Method 3: Cilium CLI
Install the Cilium CLI following the upstream instructions, then run:
cilium install \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=false \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup
cilium install \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=true \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup \
--set k8sServiceHost=localhost \
--set k8sServicePort=7445
Install Gateway API CRDs first, then:If you plan to use gRPC and GRPCRoutes with TLS, you must enable ALPN (gatewayAPI.enableAlpn=true). gRPC relies on HTTP/2, and ALPN is required to negotiate HTTP/2 support between client and server.
cilium install \
--set ipam.mode=kubernetes \
--set kubeProxyReplacement=true \
--set securityContext.capabilities.ciliumAgent="{CHOWN,KILL,NET_ADMIN,NET_RAW,IPC_LOCK,SYS_ADMIN,SYS_RESOURCE,DAC_OVERRIDE,FOWNER,SETGID,SETUID}" \
--set securityContext.capabilities.cleanCiliumState="{NET_ADMIN,SYS_ADMIN,SYS_RESOURCE}" \
--set cgroup.autoMount.enabled=false \
--set cgroup.hostRoot=/sys/fs/cgroup \
--set k8sServiceHost=localhost \
--set k8sServicePort=7445 \
--set gatewayAPI.enabled=true \
--set gatewayAPI.enableAlpn=true \
--set gatewayAPI.enableAppProtocol=true
Known issues
The following are known issues when running Cilium on Talos Linux. Each entry includes a workaround where one exists.
-
GCP internal load balancers: There are known issues when using Talos and Cilium on Google Cloud Platform with internal load balancers. See GCP ILB support / support scope local routes to be configured for details.
-
CoreDNS not working with
forwardKubeDNSToHost and bpf.masquerade: When using Talos forwardKubeDNSToHost=true (enabled by default) together with Cilium bpf.masquerade=true, CoreDNS may not work correctly. Setting forwardKubeDNSToHost=false resolves the issue. See the discussion here for more context.
-
cilium connectivity test fails with PodSecurity errors: After installing Cilium, cilium connectivity test may hang or fail with an error like:
Error creating: pods "client-69748f45d8-9b9jg" is forbidden: violates PodSecurity "baseline:latest":
non-default capabilities (container "client" must not include "NET_RAW" in securityContext.capabilities.add)
This is expected. Work around it by adding the pod-security.kubernetes.io/enforce=privileged label on the namespace level.
Additional resources
Talos has full kernel module support for eBPF. See Cilium System Requirements, Talos Kernel Config AMD64, and Talos Kernel Config ARM64.