Falco
.mdVerified against Falco 0.44.1 / falcoctl 0.13.0, installed via the official apt repo and flags verified · official docs
What it is and where it fits 🎯#
Every other tool in this Security & Compliance section is prevention-focused: it scans code, images, IaC,
or dependencies before something ships, looking for a known-bad pattern. Falco is different — it's
behavior-based runtime security, watching a live system's actual syscalls for anomalous activity: an
unexpected shell spawned inside a container, a sensitive file read, a privilege escalation. This is the
detection layer that catches a genuinely novel attack no scanner could have flagged in advance, because
there's no signature to match against — only "this doesn't look like how this workload normally behaves."
Falco runs as a long-lived daemon/DaemonSet, not a one-shot scan; falcoctl is its separate CLI for
managing rules, drivers, and OCI-distributed rule bundles.
Where runtime detection fits relative to everything upstream#
Everything to the left of Deploy is prevention — stop the bad thing from shipping. Falco is the only tool
here that assumes something might get through anyway and watches for it happening live.
Installation (Debian/Ubuntu)#
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc \
| sudo gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \
| sudo tee /etc/apt/sources.list.d/falcosecurity.list
sudo apt-get update
sudo apt-get install -y falco
falco --version
falcoctl versionRPM-based distros use the equivalent yum/dnf repo (see the official docs); a Helm chart is the standard install path for Kubernetes:
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco -n falco --create-namespaceImportant
Post-install, Falco auto-configures a driver type (modern_ebpf by default on recent kernels) via
falcoctl driver config — a kernel-driver/eBPF probe is genuinely required for live syscall capture. A
locked-down sandbox or container without kernel module access can install and configure Falco fine, but
won't receive real live events — this isn't a bug, it's the fundamental mechanism Falco depends on to see
syscalls at all.
Running Falco#
falco # foreground, using the configured driver + default ruleset
falco -c /etc/falco/falco.yaml # explicit config file
falco --dry-run # validate config and rules without processing events — good for CI-lint of a rules change
falco -M 60 # stop after 60 seconds — useful for a bounded test runInspecting rules and fields#
falco -L # list every loaded rule, name + description
falco -l "Terminal shell in container" # show one rule's full detail
falco --list # list every field available for use in rule conditions
falco --list-events # list every syscall/tracepoint event source
falco --list-plugins # show loaded pluginsReal captured falco --list-plugins output from this cheat sheet's own verification run (a fresh apt install,
one plugin loaded by default):
1 Plugins Loaded:
Name: container
Description: Falco container metadata enrichment Plugin
Contact: github.com/falcosecurity/plugins
Version: 0.7.1
Capabilities:
- Field Extraction
- Event Parsing
- Async Events
Writing a custom rule#
# /etc/falco/rules.d/custom.yaml
- rule: Unexpected shell in checkout service
desc: A shell was spawned inside the checkout container — should never happen
condition: >
spawned_process and container.name = "checkout" and proc.name in (bash, sh, zsh)
output: >
Shell spawned in checkout container (user=%user.name command=%proc.cmdline container=%container.name)
priority: WARNINGfalco --dry-run -r /etc/falco/rules.d/custom.yaml # validate the new rule file before deploying itTip
Scope custom rules to a specific workload (container.name = "checkout") rather than writing a global
rule whenever possible. A rule like "no shell spawned in any container, ever" sounds appealing but breaks
the moment any team's legitimate debugging workflow (an kubectl exec into a pod during an incident) needs
a shell — scoping to the specific workloads that genuinely should never see one avoids that false-positive
fatigue, which is the fastest way a team starts ignoring Falco alerts entirely.
falcoctl — managing rules and drivers as OCI artifacts#
falcoctl artifact install falco-rules:latest # pull the official rules bundle from the default OCI index
falcoctl driver config --type modern_ebpf # (re)configure which driver type to use
falcoctl driver install # install/load the configured driver
falcoctl registry pull oci://ghcr.io/org/falco-rules:latest # pull a custom rules bundle from your own registryOutput config (config.d snippets)#
# /etc/falco/config.d/output.yaml
json_output: true
stdout_output:
enabled: trueCombine with -o append_output.jsonoutput=true at the CLI, or -pk/-pc to append Kubernetes/container
context onto every alert line — critical in a multi-tenant cluster where "a shell was spawned somewhere" is
meaningless without knowing which namespace/pod/workload it happened in.
Real-world scenario: the checkout-service anomaly this series' own tutorial opens with#
An on-call engineer gets paged for "Terminal shell in container" on the checkout service — a workload that has no legitimate reason for anyone to open an interactive shell inside it. The temptation is to dismiss it as noise from a routine debugging session.
Caution
Investigate before dismissing. This is exactly the class of anomalous, behavior-based signal that prevention-focused tooling (scanning, RBAC) can't provide — it's specifically what runtime detection exists to surface. Check recent access/audit logs for who or what triggered it, and treat it as a potential active compromise until proven otherwise. Dismissing a genuinely novel, behavior-based security signal without investigation is exactly how real intrusions go undetected — the entire value of a tool like Falco is lost the moment a team trains itself to auto-dismiss its alerts.
Common pitfalls#
- Assuming Falco "isn't working" in a sandboxed/CI environment — see the driver requirement above; no kernel access means no live events, by design, not by bug.
- Global, unscoped rules causing alert fatigue — see the TIP above.
- Treating a Falco alert as low-priority by default — unlike a scan finding (which represents a potential issue caught before it shipped), a Falco alert represents something that actually happened on a running system, which generally warrants faster triage than a static-analysis finding.
When to reach for something else#
Falco is the only runtime-detection tool in this series — there's no direct substitute among the others here. It's a complement to, never a replacement for, the prevention-focused scanners (Trivy/Grype/Checkov/tfsec/OPA) earlier in the pipeline; a mature security posture runs both layers, not one instead of the other.