watching syscalls, catching attacks before they escalate
Image scanning finds known vulnerabilities before startup, but once a container is running, much stays invisible. Falco watches system calls directly in the kernel and detects in real time when a process inside a container behaves differently than expected, for example an unexpected shell, a write to sensitive paths, or an escape attempt.
Table of Contents
- 1. Why runtime threat detection needs its own layer
- 2. How Falco works: eBPF, kernel module and syscalls
- 3. Installing Falco and running it against Docker containers
- 4. Understanding the bundled Falco rules
- 5. Writing custom Falco rules for your environment
- 6. Alert tuning: systematically reducing false positives
- 7. Automated response to Falco alerts
- 8. Falco in production: performance and scaling
- 9. Falco compared to other approaches
- 10. Summary
- 11. FAQ
1. Why runtime threat detection needs its own layer
A fully scanned image without known CVEs is no guarantee of safety once an attacker exploits an application flaw that was not yet known at scan time. This is exactly where runtime threat detection with Falco comes in: instead of checking images before startup, Falco watches what a container actually does during its runtime and reports deviations from expected behavior in real time. An image scan is a snapshot, runtime threat detection is a continuous process.
Typical attack patterns that only become visible at runtime include starting an interactive shell in a container that should never need one, unexpected network connections to unknown hosts, or write access to sensitive system directories like `/etc/shadow`. Falco detects such patterns because it operates directly at the kernel level and sees every relevant system call regardless of which application is running inside the container. The following sections show how Falco is set up, configured and operated in production for runtime threat detection in Docker environments.
2. How Falco works: eBPF, kernel module and syscalls
Falco can observe system calls in two ways: through a classic kernel module or through eBPF, the modern and recommended approach for runtime threat detection. eBPF programs run in an isolated, verified sandbox inside the kernel and can capture syscalls, file access and network events without modifying the kernel itself. This makes Falco more resilient to kernel updates and significantly reduces the risk of kernel panics caused by faulty modules.
Every intercepted syscall is enriched by Falco with context information: container ID, image name, process name, user, and the full process hierarchy. This enrichment is decisive, because a raw syscall like `openat` says little on its own, but `openat` on `/etc/shadow` by a process named `nginx` inside a web server container is a clear warning signal for runtime threat detection. Falco compares every enriched event against a rule set and triggers an alert with a configurable severity level on a match.
3. Installing Falco and running it against Docker containers
On a Docker host, Falco itself can be run as a privileged container that needs access to the kernel, the Docker socket and the process namespaces of the host. This requirement may look paradoxical, since a privileged container contradicts the principle of least privilege at first glance, but it is unavoidable here, because Falco must observe exactly this system layer for its runtime threat detection.
After startup, Falco continuously reads the event stream and checks each event against the loaded rules. The default `falco_rules.yaml` configuration already covers common attack patterns but should not be adopted unchanged, since it is designed for a generic environment and can generate many irrelevant alerts in a specific infrastructure.
#!/usr/bin/env bash
# Run Falco as a privileged container using eBPF probe
set -euo pipefail
docker run --rm -it \
--name falco \
--privileged \
--pid host \
--net host \
-e FALCO_BPF_PROBE="" \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /dev:/dev \
-v /proc:/host/proc:ro \
-v /boot:/host/boot:ro \
-v /lib/modules:/host/lib/modules:ro \
-v /usr:/host/usr:ro \
-v ./falco_rules.local.yaml:/etc/falco/falco_rules.local.yaml:ro \
falcosecurity/falco:latest
# Watch live alerts in JSON format for downstream processing
docker logs -f falco 2>&1 | grep --line-buffered '"priority"'
4. Understanding the bundled Falco rules
Falco's default rules cover categories such as "Terminal shell in container", "Write below binary dir", "Read sensitive file untrusted", and "Contact K8S API Server From Container". Each rule consists of a condition based on syscall fields like `proc.name`, `fd.name`, or `container.id`, an output format for the alert text, and a severity level from `INFO` to `CRITICAL`. This structure makes Falco rules readable without needing to know the underlying syscall mechanism in detail.
One of the most important default rules for runtime threat detection is "Terminal shell in container", which triggers as soon as an interactive shell process is started inside a container, whether through `docker exec` or initiated directly by an attacker. In most production container environments, nobody should be opening an interactive shell, which is why this rule delivers one of the most reliable early warnings for compromised containers.
# Example Falco alert emitted by the default "Terminal shell in container" rule
{
"output": "A shell was spawned in a container with an attached terminal",
"priority": "NOTICE",
"rule": "Terminal shell in container",
"time": "2026-07-30T09:14:22.000000000Z",
"output_fields": {
"container.id": "8f3c1a9b2e77",
"container.image.repository": "shop-api",
"proc.cmdline": "bash",
"proc.pname": "docker-runc",
"user.name": "root"
}
}
5. Writing custom Falco rules for your environment
The generic default rules cover a lot, but the most valuable runtime threat detection comes from rules tailored to the concrete expected behavior of an application. For a PHP-FPM container, for example, it can be defined that outbound network connections are only allowed to known database and cache hosts, and every other connection triggers an alert. This allowlist approach is significantly more robust than trying to forbid every possible attack pattern individually.
Custom Falco rules are defined in YAML and loaded via `-r` or the local rules file. It is important to first test new rules in pure logging mode with low severity before marking them `CRITICAL` and wiring them to an alerting system, so Falco does not immediately flood the whole team with untested rules.
# Custom Falco rule: unexpected outbound connection from a PHP-FPM container
- rule: Unexpected outbound connection from PHP-FPM
desc: >
A php-fpm process opened an outbound connection to a host
that is not on the documented allowlist for this workload.
condition: >
outbound and container and
proc.name = "php-fpm" and
not fd.sip in (allowed_backend_ips)
output: >
Unexpected outbound connection from PHP-FPM
(command=%proc.cmdline connection=%fd.name container=%container.name)
priority: WARNING
tags: [network, php, custom]
- list: allowed_backend_ips
items: ["10.0.1.10", "10.0.1.20", "10.0.2.5"]
6. Alert tuning: systematically reducing false positives
The biggest practical enemy of any runtime threat detection is alert fatigue. Falco quickly generates hundreds of alerts a day on an unmodified default configuration as soon as legitimate deployment tools, health checks or CI agents trigger patterns that are actually harmless. Whoever ignores this flood eventually misses the real alerts too, because the team has stopped reading the output at all.
The systematic approach is to first test every new rule against production like logs and explicitly exclude known, harmless patterns via `macro` definitions instead of disabling the whole rule. Falco supports reusable macros with which, for example, "known CI runner processes" can be defined once and referenced across multiple rules. This keeps the rule set maintainable while the actual runtime threat detection stays sharp.
# Reusable macro to exclude known-harmless CI runner processes from alerts
- macro: known_ci_runner_processes
condition: >
proc.name in (gitlab-runner, docker-entrypoint.sh, healthcheck.sh)
- rule: Unexpected process spawned in production container
desc: A process was spawned that is not part of the expected image entrypoint chain.
condition: >
spawned_process and container and
not proc.name in (expected_app_processes) and
not known_ci_runner_processes
output: >
Unexpected process in container
(proc=%proc.name cmdline=%proc.cmdline container=%container.name image=%container.image.repository)
priority: NOTICE
tags: [process, custom]
7. Automated response to Falco alerts
Falco itself detects threats but does not actively intervene by default. For a complete runtime threat detection pipeline, Falco is therefore usually combined with `Falco Sidekick`, which forwards alerts to external systems: Slack, PagerDuty, a SIEM, or a custom webhook handler. Critical alerts such as a detected escape attempt should escalate immediately, while informational alerts can flow into a dashboard for later analysis.
For especially critical rules, for example a confirmed container escape attempt, an automated response can make sense: the affected container is stopped or isolated immediately, before a human can even react. This automation should be used very sparingly though, since a faulty rule would otherwise terminate production containers without human control, which disrupts operations more than the original alert.
#!/usr/bin/env bash
# Minimal Falco Sidekick config forwarding CRITICAL alerts to Slack
set -euo pipefail
cat > falcosidekick-config.yaml <<'EOF'
slack:
webhookurl: "https://hooks.slack.com/services/REPLACE/WITH/YOUR_WEBHOOK"
minimumpriority: "warning"
# messageformat supports host/rule placeholders, see official docs
EOF
docker run -d --name falcosidekick \
-p 2801:2801 \
-v ./falcosidekick-config.yaml:/etc/falcosidekick/config.yaml:ro \
falcosecurity/falcosidekick:latest
8. Falco in production: performance and scaling
Falco runs as one process per host and observes all containers there simultaneously, which keeps resource overhead low compared to an agent per container. With the eBPF variant, typical CPU overhead sits in the low single digit percentage range, depending on the syscall rate of the observed workloads. For runtime threat detection in environments with very high syscall density, for example database containers with intensive I/O, targeted benchmarking before a production rollout is worthwhile.
In larger Docker environments with many hosts, a central aggregation of Falco output is recommended, for example through Fluentd or directly through Falco Sidekick into a central logging system. This keeps runtime threat detection readable as one coherent picture even across ten or a hundred hosts, instead of getting lost in scattered local log files.
9. Falco compared to other approaches
Falco is not the only option for runtime threat detection, but it differs significantly from static security checks. The following table contrasts the key properties.
| Approach | Detects | Point in time | Response speed |
|---|---|---|---|
| Falco | Anomalous runtime behavior | Continuous, real time | Seconds |
| Trivy / Grype | Known CVEs in the image | Before startup (build/CI) | Not applicable, preventive |
| Docker Bench Security | Host/daemon misconfiguration | Point in time, scheduled | Hours to days |
| Log analysis (SIEM) | Patterns across many systems | After the fact | Minutes to hours |
Falco therefore closes exactly the gap that static scanners and scheduled audits leave open: the behavior of a container after it has already started. Combined with Docker Bench Security for configuration and Trivy or Grype for images, a layered runtime threat detection emerges that covers attacks in every phase of the container lifecycle.
Mironsoft
Runtime threat detection with Falco for Docker environments
Do you know what's really happening inside your containers?
We set up Falco for your Docker infrastructure, write rules tailored to your workloads, and build an alert pipeline that reports real threats instead of exhausting your team with noise.
Falco setup
eBPF based installation and integration into existing Docker hosts
Rule development
Allowlists and custom rules for your specific workloads
Alert pipeline
Build Falco Sidekick filtering and escalation levels for your team
10. Summary
Runtime threat detection with Falco closes a gap that no image scanner and no static audit can cover: the actual behavior of a container while it is running. Through eBPF, Falco observes system calls directly in the kernel, enriches them with container context, and compares them against a configurable rule set. The bundled default rules cover common attack patterns but unfold their full value only through rules tailored to the expected behavior of your own workloads.
The decisive success factor is consistent alert tuning: without systematic reduction of false positives, any runtime threat detection degrades into an ignored log flood. Whoever tests new rules in logging mode first, uses macros for known harmless patterns, and forwards critical alerts through a clear escalation chain gets a tool in Falco that detects real attacks without overwhelming the team.
Runtime Threat Detection with Falco — The essentials at a glance
How it works
eBPF captures syscalls in the kernel, Falco enriches them with container context and checks them against rules.
Rules
Default rules cover generic attack patterns, custom rules with allowlists deliver the biggest added value.
Tuning
Macros for known harmless processes prevent alert fatigue without disabling the rule itself.
Response
Falco Sidekick forwards alerts to Slack, PagerDuty or a SIEM, automated response only for clearly confirmed critical cases.