Runtime Threat Detection in Containers with Falco
AI generated
FROM
RUN
Docker · Falco · Runtime Security · Anomaly Detection
Runtime Threat Detection in Containers with Falco
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.

19 min read Syscall monitoring · custom rules · alert tuning Falco · eBPF · Docker · Kubernetes

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.

11. FAQ: Runtime Threat Detection with Falco

1What is Falco used for?
An open source tool that watches syscalls in the kernel and detects anomalies in running containers in real time.
2Why run privileged?
Access to eBPF, host process tree and Docker socket is required to see all container syscalls.
3Does it replace image scanning?
No, Falco detects runtime behavior, image scanners check known vulnerabilities before startup. Both complement each other.
4How to reduce false positives?
With reusable macros for known harmless processes instead of fully disabling the rule.
5Does Falco stop attacks automatically?
No, only with additional integration such as Falco Sidekick and custom automation.
6How high is the overhead?
With eBPF usually low single digit percentage, depending on the syscall rate of the workloads.
7Only usable with Kubernetes?
No, Falco works the same on a single Docker host as in a cluster.
8Kernel module or eBPF?
eBPF is more resilient and the recommended modern approach, the kernel module carries a higher stability risk.
9How to test new rules?
First log only at low severity, escalate only after a test phase.
10Does every environment need Falco?
For production, publicly reachable applications with sensitive data, Falco closes a critical visibility gap.