Container workloads are ephemeral, privileged by default in more ways than teams realize, and often invisible to traditional endpoint security tools. That's the gap runtime security is meant to close, and Falco — the CNCF's graduated runtime threat detection engine — is the de facto open-source standard for it. But installing Falco is the easy part. The real work is learning how to configure Falco runtime security correctly: picking the right rule set, tuning it against your actual workloads, and routing alerts somewhere your team will actually see them.
This guide walks through a practical setup: installing Falco in Kubernetes, understanding its default rules, writing custom detections for your environment, wiring up alert delivery, and tuning out noise. By the end, you'll have a working Falco deployment that catches real threats — shell access in a container, unexpected outbound connections, privilege escalation attempts — without drowning your team in false positives.
Step 1: Install Falco on Your Kubernetes Cluster
The fastest path to a working deployment is the official Helm chart. This is the core of any falco kubernetes tutorial, since almost every production Falco install today runs as a DaemonSet so every node gets its own sensor.
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
helm install falco falcosecurity/falco \
--namespace falco --create-namespace \
--set driver.kind=modern_ebpf \
--set tty=true
The modern_ebpf driver is the recommended choice on kernels 5.8+; it avoids building and maintaining a kernel module against your node images, which was historically the biggest operational headache with Falco. Verify the pods are healthy before moving on:
kubectl get pods -n falco -o wide
kubectl logs -n falco -l app.kubernetes.io/name=falco --tail=50
You should see log lines confirming the driver loaded and syscall event capture started. If you instead see permission or capability errors, jump to the troubleshooting section below.
Step 2: Understand Falco's Default Ruleset Before You Touch It
Falco ships with falco_rules.yaml, a curated set of rules covering common attacker behavior: reading credential files, spawning shells in containers, writing to sensitive system directories, disabling security tooling, and unexpected network tools launching inside a pod. Before customizing anything, pull the running config and rules so you know your baseline:
kubectl exec -n falco -it deploy/falco -- falco --dry-run -o json_output=true
kubectl get cm -n falco falco-rules -o yaml
Resist the urge to disable rules wholesale because they're noisy in a dev cluster — instead, scope them. Most default rules include a proc.name or container.image.repository exception list you can extend rather than deleting the rule entirely. This preserves runtime threat detection containers coverage for your production namespaces while quieting known-noisy dev tooling.
Step 3: Configure Falco Runtime Security Rules for Your Environment
This is the step that actually determines whether Falco is useful or just generates alert fatigue. Generic rules written for "any Kubernetes cluster" don't know that your CI runners are expected to spawn docker build processes, or that your data pipeline pods legitimately read /etc/passwd. To configure Falco runtime security policies that reflect reality, layer environment-specific overrides on top of the stock rules rather than editing them in place — this keeps upgrades clean.
Create a custom rules file mounted alongside the defaults:
# custom-rules.yaml
- macro: allowed_ci_containers
condition: (k8s.ns.name = "ci" and k8s.pod.label.role = "runner")
- rule: Unexpected Shell in Application Container
desc: Detect shell spawned in a container outside known CI runners
condition: >
spawned_process and container and
proc.name in (bash, sh, zsh) and
not allowed_ci_containers
output: >
Shell spawned in container (user=%user.name container=%container.name
image=%container.image.repository command=%proc.cmdline)
priority: WARNING
tags: [container, shell, mitre_execution]
Reference this file via Helm values so it's version-controlled and reproducible across environments:
customRules:
custom-rules.yaml: |-
<contents above>
helm upgrade falco falcosecurity/falco \
--namespace falco \
-f custom-rules-values.yaml
Ship this file through the same PR review process as application code. Runtime security rules are policy, and policy drift between clusters is exactly how detection gaps get introduced.
Step 4: Apply Falco Rules Customization for Your Actual Workloads
Generic detections catch generic attacks. The higher-value work is falco rules customization built around what your specific services do. Walk through your top five workloads and ask what "normal" looks like for each:
- Does this service ever need outbound internet access, or only cluster-internal traffic?
- Does it write to any path outside its own working directory?
- Does it ever fork a child process, or is it a single static binary?
Encode the answers as rules. For example, a service that should never make outbound connections:
- rule: Unexpected Outbound Connection from Locked-Down Service
desc: Flag any outbound network connection from a service with no legitimate egress need
condition: >
outbound and container and
k8s.pod.label.app = "internal-batch-processor" and
not fd.sip in (allowed_internal_cidrs)
output: >
Unexpected outbound connection (command=%proc.cmdline connection=%fd.name
container=%container.name)
priority: CRITICAL
Start these workload-specific rules in priority: NOTICE or WARNING during a shakeout period, review what fires for a week, then promote to CRITICAL once you've confirmed there's no legitimate traffic pattern you missed.
Step 5: Route Alerts to Somewhere Your Team Will See Them
Falco alerts that only live in pod logs get ignored. Enable the Falco sidekick or the built-in HTTP output to forward events to Slack, a SIEM, or a webhook:
falcosidekick:
enabled: true
config:
slack:
webhookurl: "https://hooks.slack.com/services/XXX/YYY/ZZZ"
minimumpriority: "warning"
helm upgrade falco falcosecurity/falco \
--namespace falco \
--set falcosidekick.enabled=true \
-f falcosidekick-values.yaml
For teams already centralizing security telemetry, forward the same events to your SIEM or data lake in parallel so Falco findings correlate against CI/CD, registry, and cloud audit log data rather than living in a silo.
Step 6: Tune Thresholds and Reduce False Positives
Expect a noisy first week. Review fired rules daily and bucket each alert into one of three outcomes: genuine finding, expected behavior that needs a scoped exception, or a rule that's fundamentally too broad for your environment. Use falco_rules.local.yaml overrides for exceptions rather than editing vendor rule files directly, so upstream updates don't silently revert your tuning.
Track your signal-to-noise ratio explicitly — if a rule fires more than a handful of times a day with zero true positives after two weeks, it needs a scoping condition, not just suppression at the alerting layer.
Verification and Troubleshooting
Confirm end-to-end detection with a controlled test rather than trusting that "no errors in the logs" means it's working:
kubectl run test-shell --image=alpine -it --rm -- /bin/sh
You should see a corresponding "Unexpected Shell" alert in your configured output channel within seconds. If nothing appears, check these common failure points:
- No events at all: Confirm the eBPF probe loaded —
kubectl logs -n falco <pod> | grep -i driver. On managed Kubernetes with custom node images (e.g., Bottlerocket, some GKE COS variants), you may need the kernel module driver instead of eBPF. - Events logged but no alerts delivered: Check
falcosidekickpod logs for delivery errors, and verifyminimumpriorityisn't filtering out the rule's priority level. - Rule not firing on expected behavior: Use
falco --dry-run -r your-rules.yaml -vlocally to lint syntax and confirm the rule compiles before deploying. - Excessive CPU usage on nodes: This usually means overly broad rules evaluating every syscall. Scope conditions to specific namespaces, container images, or process names rather than cluster-wide.
- Permission denied errors on startup: Falco needs
SYS_PTRACEandSYS_ADMINcapabilities (or eBPF equivalents) plus hostPID access — confirm your pod security policy or admission controller isn't stripping these.
How Safeguard Helps
Falco gives you the sensor; making its output actionable across a fleet of clusters, teams, and rule versions is where most organizations struggle. Safeguard ingests Falco alerts alongside your software supply chain telemetry — build provenance, SBOM data, registry scan results, and CI/CD activity — so a runtime detection like "unexpected shell in production container" is automatically correlated with what image it came from, what commit built it, and what dependencies shipped inside it.
Instead of triaging Falco alerts in isolation, your team gets the full chain of custody for the affected workload in one view, cutting investigation time from hours to minutes. Safeguard also helps standardize and version-control Falco rule customization across clusters, flagging drift when one environment's ruleset falls out of sync with your baseline policy — so runtime threat detection stays consistent as your container footprint grows.
If you're rolling out Falco across multiple clusters and want runtime signals tied directly to supply chain context, Safeguard is built to make that connection automatic.