Home DevOps Falco Runtime Security for Docker: Catch Container Attacks Before They Spread
Advanced 6 min · July 11, 2026

Falco Runtime Security for Docker: Catch Container Attacks Before They Spread

Falco runtime security for Docker containers — eBPF-based syscall monitoring, kernel module vs modern eBPF, real-time threat detection, Falco rules for container escape detection, Helm chart installation, rule customization, and incident response integration..

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Production
production tested
July 11, 2026
last updated
1,787
articles · all by Naren
Before you start⏱ 25 min
  • Docker and container fundamentals
  • Basic Linux syscall and kernel knowledge
  • Kubernetes basics for Helm chart installation
  • Familiarity with YAML and rule engines
 ● Production Incident 🔎 Debug Guide
Quick Answer

Install Falco on your Docker host with docker run --privileged -v /var/run/docker.sock:/host/var/run/docker.sock -v /proc:/host/proc:ro -v /boot:/host/boot:ro -v /lib/modules:/host/lib/modules:ro -v /usr:/host/usr:ro -v /etc:/host/etc:ro falcosecurity/falco:latest. Falco intercepts syscalls via eBPF (or kernel module) and evaluates them against rules. When something suspicious happens — container escape attempt, reverse shell, unexpected process execution — Falco outputs an alert to stdout, a file, or a gRPC endpoint. Customize rules by writing Falco rules files (.yaml) and apply them with -r /path/to/rules.yaml. Route alerts to Slack, PagerDuty, or a webhook using the falcosidekick sidecar.

✦ Definition~90s read
What is Falco Runtime Security for Docker?

Falco is a CNCF-graduated runtime security tool that detects anomalous behavior in containers and hosts by intercepting system calls at the kernel level. Originally built by Sysdig, Falco uses eBPF (extended Berkeley Packet Filter) or a kernel module to monitor every syscall made by containerized processes, evaluating them against a rich rule engine.

Think of Falco as a security camera inside your server's operating system.

When a rule is triggered — e.g., a process opens /etc/shadow inside a container, or a reverse shell is spawned — Falco generates a real-time alert that can be routed to Slack, PagerDuty, webhooks, or any OTel-compatible endpoint. With over 1400 built-in rules spanning the MITRE ATT&CK framework, Falco provides production-grade runtime visibility into container escapes, privilege escalations, crypto miners, and fileless malware — all without modifying your application code.

Plain-English First

Think of Falco as a security camera inside your server's operating system. While your Docker containers are like soundproof rooms with apps running inside, Falco watches every door and window (every system call) and knows what normal looks like. If a container suddenly tries to open a locked safe (read /etc/shadow) or whisper a secret out through the ventilation (make a reverse shell connection), Falco screams an alert. It doesn't stop the action — it's a camera, not a security guard — but it makes sure you know about the break-in within milliseconds.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

You wake up to a PagerDuty alert at 3 AM. CPU on your Kubernetes node is pegged at 100%, and a crypto miner is running inside a container that's supposed to be a stateless Node.js API. The image was scanned in CI. The base image was pinned by digest. The CVE scan passed. But nobody was watching what the container did at runtime. That's the gap Falco fills.

Container security has traditionally focused on the supply chain: scan images, sign artifacts, enforce admission policies. But once a container starts running, all that hardening can be undone by a single exploited vulnerability — a Log4Shell, an RCE in a web framework, or a compromised dependency that drops a reverse shell. Runtime security detects the attack in progress, not after the fact.

Falco is the CNCF standard for this. It's been adopted by major cloud providers, integrated into Kubernetes security stacks (CIS benchmarks, NSA Kubernetes Hardening Guide), and ships with over 1400 rules mapped to the MITRE ATT&CK framework. In this guide, you'll learn how Falco works under the hood, deploy it in production with Helm, write custom rules, route alerts to your incident response pipeline, and avoid the production pitfalls that burn teams new to runtime security.

How Falco Intercepts Syscalls: Kernel Module vs eBPF vs Modern eBPF

Falco has three driver backends for intercepting syscalls: kernel module, classic eBPF, and modern eBPF. Each has different performance characteristics, security implications, and compatibility requirements.

The kernel module (falco-probe.ko) is the oldest and most complete driver. It hooks into the kernel's syscall table directly using kprobes and tracepoints. It has the lowest overhead (1-3% CPU) but requires kernel headers to build, is tied to specific kernel versions, and must be loaded with insmod. If a kernel update happens without rebuilding the module, Falco stops working. Many organizations reject kernel modules on principle — they run in kernel space with full access to memory.

Classic eBPF uses the kernel's BPF subsystem, compiling Falco's probe into BPF bytecode that runs in a sandboxed BPF virtual machine. No kernel module loading is required — just CONFIG_BPF=y and CONFIG_BPF_EVENTS=y. Performance is slightly higher (3-5% CPU) because BPF programs are verified by the kernel before execution. This is the recommended driver for most production deployments. It survives kernel upgrades because BPF is part of the kernel tree.

Modern eBPF (introduced in Falco 0.36+) uses BPF CO-RE (Compile Once, Run Everywhere) with BTF (BPF Type Format) information. It eliminates the need for kernel headers entirely. The probe is compiled against a generic BTF file and adapts to the running kernel at load time. This is the future of Falco instrumentation — one binary that works across all kernel versions from 5.4+. Performance is comparable to classic eBPF. The tradeoff: it requires a relatively modern kernel (5.4+ for BTF support) and a BTF-enabled kernel config.

Which one to pick? If your kernel is 5.4+, use modern eBPF. Otherwise, use classic eBPF. Only fall back to the kernel module if your kernel lacks BPF support entirely (e.g., RHEL 7 or older enterprise kernels). Modern eBPF is now the default in the Falco Helm chart since version 0.37.

falco-driver-selection.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// io.thecodeforge — DevOps tutorial

# Check kernel version and BTF support
uname -r
# Example: 6.2.0-35-generic (supports modern eBPF)

# Check if BTF is available
ls /sys/kernel/btf/vmlinux
# If this file exists, modern eBPF works

# Install Falco with modern eBPF driver (Docker run)
docker run --rm -i --privileged \
  -v /var/run/docker.sock:/host/var/run/docker.sock \
  -v /proc:/host/proc:ro \
  -v /boot:/host/boot:ro \
  -v /lib/modules:/host/lib/modules:ro \
  -v /usr:/host/usr:ro \
  -v /etc:/host/etc:ro \
  falcosecurity/falco:latest \
  falco --modern-bpf

# Helm chart (recommended)
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco \
  --set driver.kind=modern_ebpf \
  --set falco.grpc.enabled=true \
  --set falco.grpc.unixSocketPath=/var/run/falco/falco.sock
Production Trap: Kernel Module on Kernel Upgrade
When the host kernel is updated, the Falco kernel module must be rebuilt. If you're not using eBPF, your Falco DaemonSet will silently stop working after a kernel update — no alerts, no errors. Always use eBPF or modern eBPF for production deployments to survive kernel upgrades without intervention.
Production Insight
The largest Falco outage I've seen was caused by a kernel module that wasn't rebuilt after a dist-upgrade on 50 nodes. Falco silently stopped producing alerts for 3 days. The team only noticed during a post-mortem after a breach simulation. Modern eBPF with CO-RE eliminates this class of failure entirely.
Key Takeaway
Modern eBPF is the production-ready Falco driver: no kernel headers, survives kernel upgrades, comparable performance to kernel modules. Requires kernel 5.4+ with BTF support.
docker-falco-runtime-security THECODEFORGE.IO Falco Runtime Security Stack Layered components from kernel to incident response Kernel Layer Kernel Module | eBPF Probe Syscall Interception Sysdig Library | libscap | libsinsp Falco Engine Rule Parser | Event Filter | Alert Generator Alert Routing Falcosidekick | Webhook | gRPC Output Notification Targets Slack | PagerDuty | Email Incident Response Container Kill | Pod Deletion | Forensics THECODEFORGE.IO
thecodeforge.io
Docker Falco Runtime Security

Falco Rules Deep Dive: Writing Custom Rules for Your Workload

Falco's rule engine is what makes it powerful. Each rule consists of a condition (a boolean expression over syscall fields), an output (a format string for the alert message), a priority, and a set of exceptions. Rules are written in YAML and loaded via -r /path/to/rules.yaml.

The condition macro is the heart of a rule. It can reference syscall fields (evt.type for syscall name, proc.name for process name, fd.name for file descriptor), container metadata (container.name, container.image), and Kubernetes labels (k8s.ns.name, k8s.pod.name). Falco supports over 200 fields across system, container, and orchestration contexts.

Falco ships with default rules organized by MITRE ATT&CK technique. You can see them at /etc/falco/rules.d/. The default rules catch container escapes (mount namespace escape, privileged container), reverse shells, crypto miners (process spawning with high CPU, network connections to mining pools), fileless execution (memfd_create), and unexpected process execution (shell inside a distroless container).

Custom rules follow a standard pattern. For example, a rule to detect any process accessing the Kubernetes service account token would use proc.name != (kubelet, kube-proxy) and fd.name startswith '/var/run/secrets/kubernetes.io/serviceaccount'. Use the append: true attribute to add exceptions to existing rules without modifying the original file — this is the cleanest way to handle false positives.

Write your custom rules in a separate file (e.g., /etc/falco/rules.d/custom-rules.yaml) and load them after the default rules. Falco merges rules alphabetically within the rules directory, so name your files to get the desired load order (e.g., 01-override.yaml for overrides, 99-custom.yaml for additions).

falco-custom-rules.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# io.thecodeforge — DevOps tutorial

# Custom Falco rules for workload-specific detection

- macro: app_container
  condition: container.image.repository in (nginx, node, python)

- rule: Shell in App Container
  desc: A shell was spawned in an application container
  condition: >
    spawned_process and container and app_container
    and proc.name in (bash, zsh, sh, dash, ash)
  output: >
    Shell spawned in app container (user=%user.name container=%container.info
    image=%container.image.repository proc=%proc.name cmdline=%proc.cmdline)
  priority: CRITICAL
  tags: [container, mitre_execution, T1059]

- rule: Shell in App Container
  append: true
  condition: and not env.HOSTNAME startswith debug-

- rule: Service Account Token Access
  desc: Detects access to K8s service account token by non-standard processes
  condition: >
    open_read and container
    and fd.name startswith /var/run/secrets/kubernetes.io/serviceaccount
    and not proc.name in (kubelet, kube-proxy)
  output: >
    SA token accessed (proc=%proc.name pid=%proc.pid container=%container.info)
  priority: WARNING
  tags: [container, kubernetes, mitre_credential_access, T1528]
Senior Shortcut: Use append Instead of Forking Rules
Never modify the default Falco rules file directly. Use the append: true attribute to add exceptions or extend conditions. This keeps your changes isolated and makes upgrades trivial — the default rules file is replaced on every Falco update.
Production Insight
A team I consulted had Falco alerting on every kubectl exec because the default rule 'Terminal shell in container' fired even for their approved debug pods. The fix was a single append: true rule that exempted pods with annotation falco.allow-shell: true. This turned a noisy alert into a clean signal.
Key Takeaway
Falco rules are YAML conditions over syscall fields. Customize using append: true to avoid forking default rules.

Alert Routing with Falcosidekick: Slack, PagerDuty, Webhook, and OTel

Falco itself outputs alerts to stdout, a file, or a gRPC endpoint. For production alert routing, you need falcosidekick — a companion component that takes Falco events and forwards them to 50+ destinations: Slack, PagerDuty, Opsgenie, Teams, Discord, webhooks, S3, GCS, Kafka, OTel, and more.

Falcosidekick runs as a sidecar or separate deployment. Falco sends events to falcosidekick via a Unix socket or TCP. Falcosidekick then applies routing rules: you can send CRITICAL alerts to PagerDuty, WARNING to Slack, INFO to a log file. It also supports deduplication, rate limiting, and muting windows.

For OTel-native observability, falcosidekick can export Falco events as OpenTelemetry logs. This integrates Falco directly into your OTel pipeline alongside application traces and metrics. Set otlp.http.endpoint or otlp.grpc.endpoint to your OTel Collector. Event severity maps to OTel log severity. Container and Kubernetes metadata are attached as OTel resource attributes.

The deployment pattern in Kubernetes: Falco as a DaemonSet with a falcosidekick sidecar per pod. Falcosidekick reads Falco events from the Unix socket and forwards them. For high-volume deployments, use a central falcosidekick service that receives events from all Falco DaemonSet pods via TCP — this gives you a single point for deduplication and routing configuration.

falcosidekick-config.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# io.thecodeforge — DevOps tutorial

slack:
  webhookurl: "https://hooks.slack.com/services/T00/B00/XXXX"
  footer: "Falco Runtime Security"
  minimumpriority: "warning"

pagerduty:
  routingkey: "your-pagerduty-routing-key"
  minimumpriority: "critical"

webhook:
  address: "http://ops-alerthandler:8080/falco"
  minimumpriority: "warning"
  customheaders: "Authorization: Bearer your-token"

otlp:
  http:
    endpoint: "http://otel-collector:4318/v1/logs"
  grpc:
    endpoint: "otel-collector:4317"
  minimumpriority: "info"

dedup:
  enabled: true
  timeout: 1m

ratelimit:
  enabled: true
  maxpersecond: 10
Interview Gold: Alert Fatigue Is the Real Risk
Teams that deploy Falco without falcosidekick routing and priority thresholds get overwhelmed by alerts within the first week. Start with CRITICAL to PagerDuty, WARNING to Slack, INFO to logs. When confidence grows, promote WARNING rules to CRITICAL as you validate.
Production Insight
The most effective Falco deployment used a 'Falco Security Score' per pod — a weighted sum of triggered rules. Pods with score >50 were automatically cordoned. This turned Falco from a passive camera into active defense without manual intervention.
Key Takeaway
Falcosidekick routes Falco events to 50+ destinations. Use priority-based routing: CRITICAL to PagerDuty, WARNING to Slack. OTel export integrates Falco into your observability pipeline.
Kernel Module vs eBPF for Falco Trade-offs in syscall interception approaches Kernel Module eBPF Kernel Compatibility Requires specific kernel headers Works across many kernels without rebuil Performance Overhead Lower overhead for high syscall rates Slightly higher due to verification Security Surface Full kernel access, larger risk Sandboxed, verified by BPF verifier Deployment Complexity Needs kernel headers and compilation Simpler, often pre-compiled Feature Set Full syscall visibility Slightly limited, but sufficient for sec Container Escape Detection Catches all escape techniques Catches most, some edge cases missed THECODEFORGE.IO
thecodeforge.io
Docker Falco Runtime Security

Container Escape Detection: What Falco Catches That Admission Controllers Miss

Kubernetes admission controllers (PodSecurity Admission, OPA/Kyverno) validate pod spec against policy before a pod runs. They check: no privileged containers, no hostPID sharing, no hostNetwork access, no volume mount of /var/run/docker.sock. But admission controllers are static — they evaluate a YAML spec. They cannot detect what a running process does.

Falco catches the dynamic attacks that admission controllers miss. The classic container escape: a process inside the container mounts the host's filesystem via mount --bind / /host, then writes an SSH key or a cron job. Admission controllers don't see this because the pod spec was compliant. But the process escalates privileges after starting.

Falco rules that catch escapes: Mount below /host (triggers on mount after a process enters the host mount namespace), ptrace inside container (process tries to trace another container's process), namespace (process creates a new namespace via unshare or clone), write below /dev (process writes to a device file), and system-user interactive (non-system user runs an interactive shell).

The most dangerous escape vector: a container that mounts the Docker socket (/var/run/docker.sock). With the socket, a process can run new containers on the host, including a privileged container that mounts the root filesystem. Falco's Launch Privileged Container rule catches this.

falco-escape-rules.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# io.thecodeforge — DevOps tutorial

- rule: Mount Below /host
  desc: a process mounted to a path below /host, indicating container escape
  condition: >
    mount and container and
    (fd.name startswith /host or fd.name startswith /hostroot)
  output: >
    Mount to host filesystem detected (proc=%proc.name container=%container.info
    target=%fd.name)
  priority: CRITICAL
  tags: [container, mitre_privilege_escalation, T1611]

- rule: Debugfs Access Inside Container
  desc: debugfs is mounted inside a container
  condition: >
    mount and container and fd.name startswith /sys/kernel/debug
  output: >
    debugfs accessed from container (proc=%proc.name container=%container.info)
  priority: CRITICAL
  tags: [container, mitre_discovery, T1087]

- rule: Write Below /etc/cron
  desc: Writing to cron files from container — persistence mechanism
  condition: >
    open_write and container and
    (fd.name startswith /etc/cron or fd.name endswith crontab)
  output: >
    Cron write detected (proc=%proc.name file=%fd.name container=%container.info)
  priority: WARNING
  tags: [container, mitre_persistence, T1053]
Production Trap: Docker Socket in Containers
Never mount /var/run/docker.sock into a container. This is the single most common escape vector. If you need container management from inside a container, use the Kubernetes API (service account) or Docker-outside-of-Docker (DooD) with controlled access.
Production Insight
During a penetration test, the red team achieved container escape in 8 minutes using a mounted Docker socket — even though the pod wasn't privileged. They launched a new privileged container that mounted the host root filesystem, extracted SSH keys, and established persistence via reverse SSH tunnel. Falco caught every step but wasn't deployed.
Key Takeaway
Admission controllers prevent known-bad configurations at deploy time. Falco catches runtime attacks — mount escapes, ptrace, namespace creation, Docker socket abuse. Both are required for defense in depth.

Falco in Kubernetes: DaemonSet Configuration with Helm

The recommended way to deploy Falco in production is the official Helm chart. It configures Falco as a DaemonSet (one pod per node), manages the driver selection, and configures falcosidekick as a sidecar. The Helm chart supports all three drivers, gRPC output, and integration with the Kubernetes API for enriching alerts with namespace and pod labels.

Critical Helm values: driver.kind (modern_ebpf, ebpf, or kernel_module), falco.grpc.enabled (true for falcosidekick integration), falco.grpc.unixSocketPath (path shared with sidecar), falco.dryRun (true for initial validation), falco.rulesFile (paths to custom rules), and resources (Falco needs 200-500m CPU and 512Mi-1Gi memory per node).

For the falcosidekick sidecar: falcosidekick.enabled: true, falcosidekick.config with your destination config. For high-volume clusters (100+ nodes), consider deploying falcosidekick as a separate Deployment with a TCP listener instead of a sidecar per DaemonSet pod.

Testing your deployment: kubectl exec -it -n falco <falco-pod> -- falco --list-events shows live syscall events. Trigger a test alert: docker run --rm -it --privileged alpine:latest sh -c 'cat /etc/shadow' on a node — Falco should emit a 'Read sensitive file untrusted' alert within seconds.

falco-helm-values.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# io.thecodeforge — DevOps tutorial

driver:
  kind: modern_ebpf

falco:
  grpc:
    enabled: true
    unixSocketPath: /var/run/falco/falco.sock
  rulesFile:
    - /etc/falco/rules.d/custom-rules.yaml
  dryRun: false

falcosidekick:
  enabled: true
  config:
    slack:
      webhookurl: "https://hooks.slack.com/services/T00/B00/XXXX"
      minimumpriority: "warning"
    pagerduty:
      routingkey: "key-here"
      minimumpriority: "critical"

resources:
  requests:
    cpu: 200m
    memory: 512Mi
  limits:
    cpu: 500m
    memory: 1Gi

tolerations:
  - operator: Exists

nodeSelector:
  kubernetes.io/os: linux
Senior Shortcut: Validate Rules Before Deployment
Use falco --dry-run -r your-rules.yaml to validate custom rules without running Falco. It checks rule syntax, macro references, and field usage. Run this in CI before deploying new rules — a syntax error can prevent Falco from starting.
Production Insight
A common pitfall: deploying Falco with default resource limits (50m CPU) and wondering why it's slow. Falco needs at least 200m per node. Monitor Falco's own metrics: falco --stats shows events per second, drops, and rule evaluation latency.
Key Takeaway
Deploy Falco via Helm as a DaemonSet with modern eBPF driver, falcosidekick sidecar, and adequate CPU (200-500m). Validate custom rules with --dry-run.

Incident Response with Falco: From Alert to Containment

Falco alerts are just the beginning. An effective incident response workflow turns those alerts into actions. The lifecycle: Falco detects anomalous behavior → alert routed via falcosidekick → responder receives notification → initial triage → containment → investigation → remediation.

Containment options: automatically cordon the node (safe, disruptive), annotate the pod (soft isolation), or kill the pod (fastest). For confirmed container escapes: immediately cordon the node, kill the offending container, and capture a forensic snapshot. For suspicious-but-not-confirmed alerts: add a network policy that restricts the pod's egress to only known endpoints, then investigate.

Falco integrates with notification platforms for the IR workflow. PagerDuty: CRITICAL alerts create incidents with the Falco output as the incident body. Slack: WARNING and INFO alerts go to #ops or #falco-alerts. Webhooks: send alerts to a custom IR tool (TheHive, Shuffle, Tines) for automated playbooks.

Automation: use falcosidekick's webhook output to trigger a serverless function that kills pods matching specific rules. For example: if Falco detects a crypto miner, automatically kill the pod and post in #security-incidents. This reduces mean-time-to-containment from minutes to seconds. But be careful — automated kill actions can become a DoS vector if an attacker triggers Falco alerts intentionally.

falco-ir-automation.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/bash
# Auto-response script triggered by falcosidekick webhook

alert_payload=$(cat)

POD_NAME=$(echo "$alert_payload" | jq -r '.output_fields.k8s.pod.name')
NAMESPACE=$(echo "$alert_payload" | jq -r '.output_fields.k8s.ns.name')
RULE=$(echo "$alert_payload" | jq -r '.rule')
PRIORITY=$(echo "$alert_payload" | jq -r '.priority')

# Auto-kill for CRITICAL crypto miner or reverse shell
if [[ "$PRIORITY" == "CRITICAL" ]] && \
   ([[ "$RULE" == *"Crypto Miner"* ]] || [[ "$RULE" == *"Reverse Shell"* ]]); then
    echo "[AUTO-KILL] Killing pod $POD_NAME in $NAMESPACE (rule: $RULE)"
    kubectl delete pod "$POD_NAME" -n "$NAMESPACE" --grace-period=30

    curl -X POST -H 'Content-type: application/json' \
      --data "{\"text\":\"Auto-killed pod $POD_NAME in $NAMESPACE for $RULE\"}" \
      https://hooks.slack.com/services/T00/B00/YYYY
fi

# Soft isolation for WARNING
if [[ "$PRIORITY" == "WARNING" ]]; then
    kubectl annotate pod "$POD_NAME" -n "$NAMESPACE" \
      "falco.incident=true" "falco.rule=$RULE" --overwrite
fi
Falco as a Smoke Detector, Not a Fire Extinguisher
Falco alerts you to a fire — it doesn't put it out. Automated responses are fire extinguishers. Design response tiers: Level 1 (automated kill for confirmed patterns like crypto miner), Level 2 (manual response for ambiguous patterns like shell in container), Level 3 (review-only for informational patterns).
Production Insight
The fastest response I've seen used Falco + falcosidekick + a Cloudflare Workers webhook. When Falco detected a crypto miner, the worker cross-referenced the pod's CPU metrics, confirmed sustained high usage, then scaled the deployment to 0 and posted a Slack message. End-to-end time: 12 seconds.
Key Takeaway
Falco alerts must feed into an incident response workflow. Tiered response: automated kill for confirmed attacks, manual for ambiguous, review-only for informational. Automate the confident cases, but always have a human review edge cases.
● Production incidentPOST-MORTEMseverity: high

The Reverse Shell That Slipped Through Every Other Security Layer

Symptom
A Node.js microservice handling user uploads suddenly consumed 8x its normal CPU. The container was OOM-killed every 20 minutes, but Kubernetes auto-restarted it, masking the pattern.
Assumption
The SRE team assumed a memory leak caused by a recent deployment. They rolled back the last three releases, ran heap dumps, and added more memory limits. The 'leak' persisted because it was a crypto miner, not a leak.
Root cause
A compromised npm dependency (image-resize@1.2.3) contained obfuscated JavaScript that downloaded a XMRig binary from a Pastebin URL, wrote it to /dev/shm, and executed it. The image scan didn't catch it because the malicious code ran at container start, not at build time. The miner used the container's network to connect to a mining pool on port 3333.
Fix
1) Killed the container and rebuilt from a clean base image without the compromised dependency. 2) Installed Falco with the default ruleset — it immediately caught the reverse shell on the next attempted attack. 3) Added a custom Falco rule to flag any process writing to /dev/shm that wasn't a known application binary. 4) Pinned dependency versions with npm audit and added runtime policy that blocks outbound connections on non-standard ports.
Key lesson
  • Image scanning catches known CVEs at build time but cannot detect malicious runtime behavior. Runtime security with Falco fills this gap.
  • A compromised dependency can execute arbitrary code at container start — scan is not enough. You must monitor syscalls at runtime.
  • Falco's default ruleset catches reverse shells, crypto miners, and fileless execution out of the box. Deploy it before you need it.
Production debug guideSymptom → Root cause → Fix3 entries
Symptom · 01
Falco prints 'Error: detector error' or 'cannot open /dev/falco0' on startup
Fix
1. Verify Falco has required capabilities: SYS_ADMIN, SYS_PTRACE, SYS_RESOURCE. 2. If using kernel module, ensure kernel headers are installed: apt install linux-headers-$(uname -r). 3. If using eBPF, check CONFIG_BPF is enabled: zcat /proc/config.gz | grep CONFIG_BPF. 4. Fall back to userspace instrumentation with --userspace if kernel instrumentation fails.
Symptom · 02
Falco triggers false positives for normal container operations (package installs, cron jobs, init scripts)
Fix
1. Check the specific rule name in the Falco alert. 2. Add exceptions to the rule using the append macro: - rule: Run shell in container, append: true, condition: and not proc.name in (apt, yum, apk). 3. Use Falco's priority system: set sensitive rules to WARNING instead of CRITICAL. 4. Test rules in Falco's dry-run mode: falco -T.
Symptom · 03
Falco doesn't detect container escape attempts that you know are happening
Fix
1. Verify Falco is receiving syscalls from the correct sources: falco --list-syscalls. 2. Check if the attack path uses syscalls that are filtered by the default macro. 3. Add specific syscall monitoring: - macro: my_escape_attempt, condition: evt.type in (ptrace, mount, umount2, init_module). 4. Ensure Falco runs with --pidns to see containers with separate PID namespaces.
FeatureFalcoSeccompAppArmorSELinux
Detection vs PreventionDetection + optional preventionPrevention onlyPrevention onlyPrevention only
Behavior scopeSyscall-level anomaly detectionSyscall allowlist/blocklistFile path + network access controlLabel-based MAC
Rule languageYAML conditions (macro-based)JSON profile (allowlist)Text profilePolicy module
K8s awarenessNative (pod, namespace, labels)Native (RuntimeDefault)Manual (per-pod annotation)Manual (context)
Performance impact1-5% CPU<1% CPU<1% CPU1-3% CPU
Best forRuntime threat detectionDefault syscall restrictionFile/network access controlMulti-level security
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
falco-driver-selection.shuname -rHow Falco Intercepts Syscalls
falco-custom-rules.yaml- macro: app_containerFalco Rules Deep Dive
falcosidekick-config.yamlslack:Alert Routing with Falcosidekick
falco-escape-rules.yaml- rule: Mount Below /hostContainer Escape Detection
falco-helm-values.yamldriver:Falco in Kubernetes
falco-ir-automation.shalert_payload=$(cat)Incident Response with Falco

Key takeaways

1
Falco provides runtime security for containers by intercepting syscalls via eBPF. Use modern eBPF driver for kernel 5.4+
survives kernel upgrades without module rebuilds.
2
Customize Falco rules with `append
true` to avoid forking default rules. Route critical alerts to PagerDuty, warnings to Slack, via falcosidekick.
3
Falco catches container escape attempts (mount namespace, ptrace, Docker socket) that admission controllers miss. Both are required for defense in depth.
4
Deploy Falco via Helm as a DaemonSet. Allocate 200-500m CPU per node. Validate custom rules with --dry-run before applying.
5
Build a tiered incident response
automated pod kill for confirmed attacks, manual response for ambiguous patterns, review-only for informational alerts.

Common mistakes to avoid

4 patterns
×

Deploying Falco with the kernel module and not rebuilding after kernel upgrade

×

Not configuring falcosidekick and letting Falco output to stdout only

×

Running all Falco rules at CRITICAL priority, causing alert fatigue

×

Not adjusting Falco rules for your specific workload, accepting all false positives

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain how Falco uses eBPF to intercept syscalls without modifying the ...
Q02SENIOR
Your Falco deployment is missing container escapes despite default rules...
Q03SENIOR
Design a runtime security strategy for a multi-tenant Kubernetes cluster...
Q04SENIOR
How would you test that Falco is working in production without triggerin...
Q01 of 04SENIOR

Explain how Falco uses eBPF to intercept syscalls without modifying the kernel.

ANSWER
Falco's eBPF driver compiles a BPF program that attaches to tracepoints and kprobes in the kernel. The BPF program runs in a sandboxed BPF virtual machine — verified by the kernel's BPF verifier to ensure it terminates and doesn't corrupt kernel memory. A kernel module runs in kernel space with full privileges and can crash the kernel. eBPF is safer because the verifier rejects programs that could panic the kernel. Tradeoff: eBPF programs are more limited (no unbounded loops, limited stack) and have slightly higher overhead.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What's the difference between Falco and a traditional IDS like Snort or Suricata?
02
Can Falco block an attack, or does it only alert?
03
Does Falco work with containerd/cri-o or only with Docker?
04
What is the performance overhead of running Falco in production?
05
How do I update Falco rules without redeploying?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Drawn from code that ran under real load.

Follow
Verified
production tested
July 11, 2026
last updated
1,787
articles · all by Naren
🔥

That's Docker. Mark it forged?

6 min read · try the examples if you haven't

Previous
Docker Secrets Management
33 / 41 · Docker
Next
Rootless Docker Production Guide