Most Suricata deployments fail the same way: someone enables every ruleset, points it at a busy link, and within a day the alerts are an unread firehose and the box is dropping packets it cannot inspect fast enough. Suricata is excellent, but out of the box it is tuned for nothing in particular. Making it useful is about constraining it — the right mode, the right rules, and threading that matches your hardware.
IDS vs IPS: Decide First
The single biggest decision, because it changes the deployment and the blast radius of a mistake:
- IDS (passive) — Suricata sees a copy of traffic (SPAN port, TAP, or AF_PACKET in tap mode). It alerts and logs but cannot block. A bad rule is noise.
- IPS (inline) — traffic flows through Suricata, which can
droppackets. A bad rule is an outage.
Start in IDS. Run it passive for weeks, tune the rules, and only go inline once you trust the ruleset — because inline, a false positive on a drop rule blocks legitimate traffic, and now your security tool is the incident.
Passive IDS with AF_PACKET
For IDS, AF_PACKET in tap mode reads from a mirror/SPAN interface with no kernel overhead from a capture library:
af-packet: - interface: eth1 # SPAN/mirror port threads: 4 cluster-id: 99 cluster-type: cluster_flow defrag: yes use-mmap: yes tpacket-v3: yescluster_flow hashes packets to threads by flow, so both directions of a connection land on the same worker — essential, because Suricata reassembles streams and needs to see the whole flow on one thread.
suricata -c /etc/suricata/suricata.yaml -i eth1Inline IPS: AF_PACKET Bridge or NFQUEUE
Two ways to put Suricata in the path:
AF_PACKET inline bridges two interfaces — traffic in eth1 out eth2 and back, with Suricata between. Add a copy-iface peer:
af-packet: - interface: eth1 threads: 4 cluster-id: 98 cluster-type: cluster_flow copy-mode: ips copy-iface: eth2 - interface: eth2 threads: 4 cluster-id: 97 cluster-type: cluster_flow copy-mode: ips copy-iface: eth1NFQUEUE hooks Suricata into netfilter — you choose exactly which traffic to inspect with an iptables/nftables rule, which is powerful on a Linux router:
# Send forwarded traffic to queue 0nft add rule inet filter forward queue num 0suricata -c /etc/suricata/suricata.yaml -q 0NFQUEUE is the more flexible option on a host already doing routing/firewalling — you inspect only what matters (e.g., only inbound to the DMZ) instead of everything, which is half the performance battle.
Rulesets: Less Is More
The instinct to enable every ruleset is exactly wrong. Thousands of rules you do not need cost CPU on every packet and bury the alerts that matter. Manage rules with suricata-update:
# List available sourcessuricata-update list-sources
# Enable a curated source (free ET Open)suricata-update enable-source et/open
# Updatesuricata-update
# Reload rules without restarting (no traffic interruption)suricatasc -c reload-rulesThen prune. Disable rule categories irrelevant to your environment, and use threshold/suppression for the noisy-but-not-actionable:
# Suppress a rule entirely for a known-good scannersuppress gen_id 1, sig_id 2013028
# Rate-limit a chatty rule: alert once per 60s per sourcethreshold gen_id 1, sig_id 2019401, type limit, track by_src, count 1, seconds 60A tuned 8,000-rule set that alerts on real things beats a 40,000-rule set you have learned to ignore. Suricata’s value is the alert you actually read.
Alert, Then Selectively Drop
In IPS mode, a rule’s action matters. ET rules ship as alert; switch only the ones you trust to drop. Rather than editing rules, override actions by SID:
# suricata-update with a modify list to flip specific SIDs to drop# /etc/suricata/enable.conf + modify.conf# modify.conf: turn a trusted signature into a drop2013028 "alert (.+)" "drop \1"The safe path inline: everything stays alert first, you watch the eve.json logs for what would have dropped, and you promote a signature to drop only after confirming it never fires on legitimate traffic. Going straight to drop on the full ruleset is how Suricata blocks your own backups or a SaaS integration and gets blamed for an outage it was technically right about.
Performance: Match Threads to Reality
Suricata is multi-threaded, but you have to feed it correctly:
- Workers runmode (default with AF_PACKET clustering) — each thread does capture-to-detection for its flows. Set
threadsto your physical cores, not hyperthreads, and pin withcluster_flow. - Watch the drops. The metric that matters is
capture.kernel_drops— packets that arrived but Suricata could not inspect in time. Non-zero means it is overwhelmed and not seeing all traffic, which silently defeats the point.
# Live stats — kernel_drops must stay at/near zerosuricatasc -c dump-counters | grep -E 'kernel_packets|kernel_drops'
# Or read stats.log / eve.json stats eventstail -f /var/log/suricata/stats.log | grep -E 'capture.kernel'If drops climb: reduce rules, add worker threads, enable hardware offload appropriately, or split the traffic across more capture interfaces. A Suricata dropping 5% of packets is a Suricata missing 5% of attacks while looking perfectly healthy in the dashboard.
The eve.json Pipeline
Suricata’s real output is eve.json — structured events (alerts, flows, DNS, TLS, HTTP, file extraction) you ship to a SIEM or Elastic. The alert is the headline; the flow/DNS/TLS records are the context that makes an alert investigable:
outputs: - eve-log: enabled: yes filetype: regular filename: eve.json types: - alert - dns - tls - http - flowShip eve.json, build dashboards on it, and alert on the signatures you tuned. An IDS whose output nobody pipes anywhere is a CPU-burning logfile.
Tuning the Engine, Not Just the Rules
Dropping packets is not always a rule-count problem. Suricata’s defaults assume a modest link, and three knobs decide whether it keeps up before you ever touch the ruleset.
Stream reassembly memory. On a busy link Suricata tracks far more concurrent flows than the defaults allow, and when the flow or stream tables fill it starts bypassing packets — which looks like a detection gap, not an error:
flow: memcap: 1gb hash-size: 262144 prealloc: 100000
stream: memcap: 4gb reassembly: memcap: 8gb depth: 1mb # bytes per flow reassembled; raise for large file transfersMPM matcher. The multi-pattern matcher is the hot path for every packet. On modern x86 with the rule volume of a real ruleset, hyperscan is dramatically faster than the ac default:
mpm-algo: hyperscanWatch the memory-pressure counters alongside kernel_drops — these reveal a different failure than raw CPU saturation:
suricatasc -c dump-counters | \ grep -E 'flow.memcap_pressure|flow.emerg_mode_entered|tcp.reassembly_memuse|tcp.ssn_memcap_drop'# Rising flow.memcap_pressure / non-zero emerg_mode_entered / ssn_memcap_drop = raise the memcaps, not the thread countA box at 40% CPU still dropping packets is almost always memcap starvation, not a threading shortfall. Adding worker threads there wastes cores; raising the memcaps fixes it.
When a Drop Rule Causes an Outage: The Drill
Before trusting inline, rehearse the failure you most fear — a drop rule blocking legitimate traffic — so you know exactly how to find and reverse it under pressure. Run inline with a single deliberately broad drop, generate traffic that trips it, then trace and unblock.
# Find what dropped, from the structured log — verdict + signaturegrep '"action":"blocked"' /var/log/suricata/eve.json | \ jq '{sig: .alert.signature, sid: .alert.signature_id, src: .src_ip, dest: .dest_ip}'That signature_id is the lever. The fastest reversal that does not require a restart is to suppress the SID and hot-reload:
# /etc/suricata/threshold.config — stop the offending SID firing at allsuppress gen_id 1, sig_id 2013028suricatasc -c reload-rules # applies without dropping the inline pathThe slower-but-cleaner fix is to flip the signature back from drop to alert in modify.conf and re-run suricata-update, but in an active outage suppress + reload-rules stops the bleeding in seconds with no traffic interruption. The lesson of the drill: every inline drop needs a known, rehearsed off-switch, and reload-rules — not a restart — is it, because a restart on a bridge or NFQUEUE path drops live traffic while it comes back.
The Honest Summary
Suricata becomes useful when you stop treating it as “turn on all the rules and watch the screen.” Run it passive first, enable a curated ruleset and prune hard, watch kernel_drops like a hawk, and promote rules to drop one trusted signature at a time. Do that and it is a sharp instrument. Skip it and you get the common outcome: a box at 100% CPU, dropping packets it cannot inspect, generating alerts nobody reads — secure on paper, blind in practice.