How to Deploy Falco Runtime Security on Kubernetes with eBPF, Custom Rules, and Alerting
Runtime attacks don’t stop at the perimeter. They show up as a shell inside a container, a service account token read by the wrong process, or a privileged pod doing something it shouldn’t. Falco runtime security on Kubernetes watches for exactly live kernel and container behavior, not just configuration.
This guide covers a full install with Helm and the modern eBPF driver, Falcosidekick alerting, and four custom detection rules for a real cluster.
Table of Contents
Why Falco Runtime Security on Kubernetes Matters
Most Kubernetes hardening stops at two layers:
- Admission controllers (Kyverno, OPA/Gatekeeper) block bad pod specs.
- Network policies limit traffic between pods.
Neither one tells you what a process is actually doing inside a container after it starts. That’s the gap Falco runtime security on Kubernetes fills; it uses a kernel-level driver to watch system calls in real time with a rules engine.
If you’ve already deployed K3s on Ubuntu 24.04 and layered a SIEM like Wazuh for your VPS infrastructure, Falco is the missing runtime security part. Wazuh gives you host-level and log-based detection, while Falco gives you kernel-level, container-aware detection scoped to Kubernetes workloads specifically.
Prerequisites
Before deploying Falco runtime security on Kubernetes, make sure you have:
- A Kubernetes cluster (K3s, kubeadm, EKS, GKE, AKS) with Linux nodes, x86_64 or ARM64. Docker Desktop won’t work; Falco needs direct kernel access.
- kubectl and Helm v3 are installed and pointed at your cluster.
- Kernel 5.8+ on every worker node for the modern eBPF driver.
- BTF support at
/sys/kernel/btf/vmlinuxon each node. Ubuntu 22.04/24.04 include this by default. - Cluster-admin access, since Falco runs privileged to load its eBPF probe and read
/proc,/dev, and the container runtime socket. - Optional: a Slack webhook, email relay, or other
Falcosidekickoutput for real alert routing.
Understand Falco’s Architecture and the eBPF Driver
Falco runtime security on Kubernetes has three main parts, which are worth knowing before you touch any config:
- The Falco engine: Runs on every node as a pod. It watches what’s happening, such as process starts, file reads, and network activity, adds Kubernetes details like pod and namespace, checks it with your rules, and sends an alert if something matches.
- The driver: The piece that actually reads this activity from the kernel. Since Falco 0.38.0, the modern eBPF driver is the default. It works on any modern kernel without needing to build or match anything to your specific kernel version. Since Falco 0.41+, the old kernel-module method isn’t recommended anymore, and the older eBPF probe is being deprecated; modern eBPF is the one to use.
- Falcosidekick: A small helper service that takes Falco’s alerts and sends them to places like Slack, Teams, email, or a dashboard. It’s optional, but almost everyone uses it.
Since the modern eBPF driver is built into Falco itself, there’s no extra setup step and nothing to build. That’s why it’s the right default for any Falco runtime security on Kubernetes setup.
Step 1. Prepare Kubernetes Cluster
Confirm your nodes meet the eBPF requirements before installing anything:
uname -r
# should report 5.8 or later, ideally 5.15+
ls /sys/kernel/btf/vmlinux
# should exist; if missing, modern eBPF cannot load and you'd need the kernel module instead
Then add the official Helm repository, which is the same source used for every Falco runtime security on Kubernetes deployment in this guide:
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
Confirm the chart is available and check its current version before installing:
helm search repo falcosecurity/falco
Step 2. Install Falco with Helm Using the Modern eBPF Driver
Create a dedicated namespace and install the chart with the modern eBPF driver. Setting driver.kind=modern_ebpf is a good practice, even though it’s already the default. It makes your choice clear in the config, so if an old values file ever overrides the default later, you’re still protected:
helm install falco falcosecurity/falco \
--namespace falco \
--create-namespace \
--set driver.kind=modern_ebpf \
--set tty=true
The --set tty=true flag just makes Falco’s logs easier to read in kubectl logs; it doesn’t affect detection at all. This one command is the core of any Falco runtime security on Kubernetes setup; everything else in this guide builds on top of it.
Step 3. Verify the Deployment
At this point, you must check that a pod is running on every eligible node, and that each one is healthy:
kubectl get pods -n falco -o wide
kubectl wait pods --for=condition=Ready --all -n falco --timeout=120s
Tail the logs to confirm the modern eBPF probe loaded correctly:
kubectl logs -n falco -l app.kubernetes.io/name=falco -c falco | grep -i "modern bpf"
You should see a line saying the modern BPF engine started and attached its probes. If a pod is stuck in CrashLoopBackOff, it’s because that node’s kernel doesn’t support BTF. You must check dmesg on that node for BPF errors.
Step 4. Enable Falcosidekick for Alert Routing
Reading kubectl logs works for a demo, but not for real use. Upgrade the release to enable Falcosidekick, which becomes the alert routing for this Falco runtime security on Kubernetes setup:
helm upgrade --namespace falco falco falcosecurity/falco \
--set driver.kind=modern_ebpf \
--reuse-values \
--set falcosidekick.enabled=true \
--set falcosidekick.webui.enabled=true
Confirm the new services came up:
kubectl -n falco get svc
You should see falco-falcosidekick, port 2801, receiving events from Falco, and falco-falcosidekick-ui, port 2802, serving the dashboard.
Step 5. Deploy Falcosidekick UI
Port-forward the UI to your local machine to check that events are coming through before setting up a permanent output:
kubectl -n falco port-forward svc/falco-falcosidekick-ui 2802:2802
Open http://localhost:2802 and sign in with the default admin credentials. Change the default admin password immediately. This UI is useful for a small cluster, but it’s meant for quick checks, not as your permanent record of alerts.
Step 6. Write Custom Detection Rules
Falco comes with good default rules, but Falco runtime security on Kubernetes gets real value when you add rules built for your own workloads. You must create a file called falco-custom-rules.yaml and fill it in with the four rules below. Each one is meant to work alongside Falco’s default rules, not replace them.
nano falco-custom-rules.yaml
Detect a Shell Inside a Container
A shell running inside a container is one of the earliest warning signs that something’s wrong. Falco already has a built-in rule for this called “Terminal shell in container.” The custom version below adds one exception: it ignores your known kubectl debug, but still catches everything else.
customRules:
rules-shells.yaml: |-
- rule: Unexpected Shell in Container
desc: Detects an interactive shell spawned inside a container outside of an approved debug workflow
condition: >
spawned_process and container and shell_procs
and not proc.pname in (runc, containerd-shim)
and not k8s.ns.name in (kube-system)
and not container.image.repository in (approved_debug_images)
output: >
Unexpected shell in container (user=%user.name shell=%proc.name parent=%proc.pname
cmdline=%proc.cmdline container_id=%container.id container_name=%container.name
image=%container.image.repository k8s_ns=%k8s.ns.name k8s_pod=%k8s.pod.name)
priority: NOTICE
tags: [container, shell, mitre_execution]
- list: approved_debug_images
items: []
Detect Privileged Pods at Runtime
Admission controllers should already block most privileged pod specs before they schedule, but Falco runtime security on Kubernetes gives you a second and independent check the moment a privileged container starts. It is useful for catching misconfigured policies and other things:
customRules:
rules-privileged.yaml: |-
- rule: Privileged Pod Started Unexpectedly
desc: Detects the start of any privileged container outside of a known trusted allowlist
condition: >
container_started and container.privileged=true
and not container.image.repository in (trusted_privileged_images)
and not k8s.ns.name in (kube-system, falco)
output: >
Privileged container started (user=%user.name command=%proc.cmdline
image=%container.image.repository:%container.image.tag
container_id=%container.id k8s_ns=%k8s.ns.name k8s_pod=%k8s.pod.name)
priority: WARNING
tags: [container, privileged, mitre_privilege_escalation]
- list: trusted_privileged_images
items: []
Detect Sensitive-File Reads
Falco’s default rules already catch reads of /etc/shadow and similar system files. This custom rule adds coverage for Kubernetes secrets, mounted service account tokens, and .env files. These are common targets once an attacker gets inside, and this is one of the most useful rules you can add to Falco runtime security on Kubernetes.
customRules:
rules-sensitive-files.yaml: |-
- list: k8s_sensitive_files
items:
[
/var/run/secrets/kubernetes.io/serviceaccount/token,
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt,
/root/.kube/config
]
- rule: Sensitive Kubernetes File Read by Unexpected Process
desc: Detects reads of Kubernetes service account tokens or kubeconfig by a process other than the expected runtime
condition: >
open_read and container and fd.name in (k8s_sensitive_files)
and not proc.name in (kubelet, containerd, dockerd)
output: >
Sensitive Kubernetes file read (file=%fd.name process=%proc.name
cmdline=%proc.cmdline container_id=%container.id container_name=%container.name
k8s_ns=%k8s.ns.name k8s_pod=%k8s.pod.name)
priority: WARNING
tags: [filesystem, secrets, mitre_credential_access]
Detect Unexpected Network Tools
Falco already has a rule called “Launch Suspicious Network Tool in Container” that flags common tools such as nc, nmap, and tcpdump. Add more tools to the list and skip your own network-diagnostics namespace so this part of the Falco runtime security on Kubernetes doesn’t alert on your own team’s work.
customRules:
rules-network-tools.yaml: |-
- list: extra_network_tool_binaries
items: [socat, ngrep, mitmproxy, zmap, hping3]
append: true
- rule: Launch Suspicious Network Tool in Container
append: true
condition: >
and not k8s.ns.name in (network-diagnostics)
override:
condition: append
Step 7. Load Custom Rules and Restart Falco
Apply all four custom rule files in a single upgrade, so Falco picks them up on the next pod restart. This is the point where your Falco runtime security on Kubernetes deployment stops being generic and starts reflecting your actual environment:
helm upgrade --namespace falco falco falcosecurity/falco \
--reuse-values \
-f falco-custom-rules.yaml
Wait for all the pods to restart, then check that each one loaded the new rules without an error:
kubectl wait pods --for=condition=Ready --all -n falco --timeout=120s
kubectl logs -n falco -l app.kubernetes.io/name=falco -c falco | grep -i "rule"
If your YAML rule file has a mistake, you’ll see a startup error here, and the pod will keep crashing and restarting. Always check this log first; don’t assume a rule is not matching when the pod might not even be loading it.
Step 8. Generate and Check Test Alerts
Now you should test each rule with a real workload. Use a disposable target:
kubectl create deployment nginx-test --image=nginx
kubectl wait --for=condition=available deployment/nginx-test --timeout=60s
Run the shell rule:
kubectl exec -it $(kubectl get pods -l app=nginx-test -o name) -- /bin/sh
Run the sensitive-file rule:
kubectl exec -it $(kubectl get pods -l app=nginx-test -o name) -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
Run the network-tool rule. Install a tool first since the nginx base image is minimal:
kubectl exec -it $(kubectl get pods -l app=nginx-test -o name) -- sh -c "apt-get update && apt-get install -y netcat-openbsd && nc -l -p 4444"
Run the privileged-pod rule:
kubectl run privileged-test --image=busybox --privileged --restart=Never -- sleep 300
For each one, confirm the alert both in the logs and in the UI:
kubectl logs -n falco -l app.kubernetes.io/name=falco -c falco | grep -Ei "warning|notice"
Now check the Events tab in the Falcosidekick UI. You should see all four alerts show up, each with details like pod name, namespace, image, and the command that ran; these come from the output text you set in each rule.
Testing alerts this way, using workloads you triggered on purpose, is the quickest way to make sure Falco runtime security on Kubernetes is set up correctly before you trust it in production.
Clean up the test resources once you’ve confirmed detection:
kubectl delete deployment nginx-test
kubectl delete pod privileged-test
Step 9. Reduce Noise with Rule Exceptions
Every rule above will eventually trigger on something normal, a CI runner starting a shell, or an ops pod running tcpdump on purpose. Falco’s exceptions feature lets you limit a rule so it skips those known cases, without turning the rule off completely. That’s the right way to tune Falco runtime security on Kubernetes, not by disabling detections, but by making them more specific.
customRules:
rules-exceptions.yaml: |-
- rule: Unexpected Shell in Container
exceptions:
- name: ci_runner
fields: [k8s.ns.name, container.image.repository]
comps: [=, startswith]
values:
- [ci-runners, registry.internal/ci/]
override:
exceptions: append
Notice the override: exceptions: append block. You need this exact syntax because Falco 0.36 replaced the older append: true shortcut for changing rules. Without it, Falco thinks you’re defining a duplicate rule, logs a warning, and doesn’t merge your exception.
Apply it:
helm upgrade --namespace falco falco falcosecurity/falco \
--reuse-values \
-f falco-custom-rules.yaml \
-f falco-exceptions.yaml
Step 10. Route Output to a Production Destination
The web UI is fine for quick checks, but a real Falco runtime security on Kubernetes setup needs alerts to land where your team already works. Falcosidekick can send alerts to Slack, Microsoft Teams, email, Elasticsearch, Loki, PagerDuty, and many other tools. Here’s an example using Slack:
helm upgrade --namespace falco falco falcosecurity/falco \
--reuse-values \
--set falcosidekick.webui.enabled=false \
--set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/YOUR/WEBHOOK/URL" \
--set falcosidekick.config.slack.minimumpriority="notice"
The minimumpriority setting matters more than it looks. Falco ranks alerts from most to least serious:
emergency, alert, critical, error, warning, notice, informational, debug
Setting minimumpriority: notice sends everything at notice level or higher, while filtering out low-value informational and debug noise, which is a good default for a Slack channel people actually read.
Send the more serious alerts, WARNING and above, to a paging tool like PagerDuty separately, and keep the full noisy stream in Elasticsearch or Loki for later searching.
Falco Upgrade Checklist and Maintenance Tips
Falco updates often, and both the rule format and driver setup have changed quite a bit over time. So don’t just run helm upgrade; go through this checklist first for any Falco runtime security on Kubernetes setup you depend on:
1. Check your current version before upgrading:
helm search repo falcosecurity/falco --versions | head
2. Read the release notes for rule changes. The override block replaced the old append: true shortcut, and newer versions handle container info through plugins instead.
3. Run helm diff upgrade, using the helm-diff plugin, before applying, since default settings for the driver and Falcosidekick can change between versions.
4. After upgrading, redo the test-alert steps from Step 8. A rule-format change can quietly break a custom rule even if the pod looks healthy.
5. Check kubectl logs on the Falco pods right after upgrading for deprecation warnings; they’re your earliest sign that something you rely on is being deprecated.
Final Words
You now have a working Falco runtime security on Kubernetes setup, including the modern eBPF driver installed with Helm, Falcosidekick sending alerts to both a UI and a real destination, and four custom rules that catch shells in containers, privileged pods, sensitive file reads, and unexpected network tools. Each rule was tested on a real workload, and you can fine-tune them with exceptions instead of turning them off.
We hope you enjoy this guide. You can run your eBPF-capable Kubernetes worker nodes on PerLod dedicated servers to monitor container runtime behavior.
For more detailed information, check the Falco Kubernetes quickstart documentation.
FAQs
Does Falco need a kernel module to work?
No. The modern eBPF driver has been the default since Falco 0.38.0. It needs no kernel module or headers. It just needs kernel 5.8+ with BTF support.
Can Falco replace network policies or admission controllers?
No. Falco detects runtime behavior after a pod is already running; it doesn’t block scheduling or restrict traffic, so it works best alongside those controls, not instead of them.
Is the Falcosidekick UI safe to expose outside the cluster?
Not with default credentials. Change the admin login immediately or keep it behind kubectl port-forward and a VPN.