The weekend after I upgraded a client's firewall to OPNsense 25.1.10 last July, the bouncer log had turned into a wall of the same line: dial tcp 127.0.0.1:8080: connect: connection refused, repeating every ten seconds since Friday night. The firewall had been silently enforcing nothing for two days. That incident produced the checklist below, which walks the whole chain on OPNsense 25.x in the order things depend on each other: plugin, services, LAPI (Local API), bouncer, pf tables and finally the detections themselves. Start at the top even if you think you know where the body is buried. Especially then.
os-crowdsec missing from the plugin list
If you can't even find the plugin, check the firmware settings first. os-crowdsec is community-maintained, and current OPNsense versions hide community plugins until you tick the option to show them under System > Firmware > Settings. Once visible, installing os-crowdsec pulls three packages: the plugin itself, crowdsec and crowdsec-firewall-bouncer.
One rule saves a lot of grief later: the plugin owns these services. The official install documentation is explicit that you shouldn't enable or start them from the shell the way you would on a plain FreeBSD box. Every install I've seen where someone had hand-edited rc.conf ended up in a state where the GUI and reality disagreed about what was running.
Services > CrowdSec > Overview as the first stop
Before touching a config file, open Services > CrowdSec > Overview. It shows the service states, the registered machines and bouncers and the installed hub items in one screen. You're looking for two green services (the Log Processor, formerly called the IDS, and the firewall bouncer, formally a Remediation Component) plus a bouncer whose last pull is recent. A bouncer that registered once and last pulled days ago tells you the LAPI link is broken even before you read a single log line. The Settings tab next door controls which of the components are enabled, so confirm nobody helpfully unticked LAPI during the last change window.
Fix connection refused from the firewall bouncer
Back to that July failure class, which is common enough after upgrades that it earned its own GitHub issue against OPNsense 25.1.10. The bouncer log lives at /var/log/crowdsec/crowdsec-firewall-bouncer.log, and connection refused there means exactly one thing: nothing is answering on the address the bouncer dials. Check who's listening:
sockstat -l | grep 8080
If nothing is bound to 127.0.0.1:8080, the LAPI isn't up. Read /var/log/crowdsec/crowdsec.log and /var/log/crowdsec/crowdsec_api.log for the reason; a port already taken by another service and leftovers from a partial upgrade are the two I keep meeting. If something else entirely holds the port, evict it or move CrowdSec's listen_uri in /usr/local/etc/crowdsec/config.yaml, keeping local_api_credentials.yaml and the bouncer's api_url pointed at the same place.
A detail that confuses people mid-incident: cscli lapi status can report success while the bouncer fails, since the two read their target URLs from different files. In the 25.1.10 issue linked above the reporter saw exactly that, and the root cause turned out to be a config error on their side. The lesson generalizes. When one client connects and another refuses, diff the URLs they're each configured to dial before blaming the service.
API error: access forbidden after an upgrade
A different flavor of broken: the bouncer reaches the LAPI but gets rejected with API error: access forbidden. That's an authentication failure, and it usually means the API key in the bouncer's config no longer matches what the LAPI has registered, a state upgrades are good at manufacturing. On OPNsense the plugin normally handles bouncer registration itself, so my first move is the boring one: reinstall or reset the plugin and let it re-register cleanly rather than hand-surgering keys.
If you'd rather fix it manually, the sequence is short. List what the LAPI knows with cscli bouncers list, register a fresh one, then put the key where the bouncer reads it:
cscli bouncers add opnsense-fw-bouncer
Copy the printed key into the api_key field of /usr/local/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml and restart the services from the GUI. Be aware the plugin can regenerate that file during its own maintenance, which is the argument for letting it own registration in the first place. The same 403 shows up on plain Linux installs too, and the access forbidden fix guide digs into the LAPI side of it beyond what OPNsense needs.
Verify detection with cscli metrics and alerts
With the plumbing green, prove the Log Processor is seeing traffic at all:
cscli metrics show acquisition
cscli metrics show decisions
cscli alerts list
The acquisition table is the honest one. Lines read but nothing parsed means the parsers don't match your log format, while no lines read at all means the acquisition files under /usr/local/etc/crowdsec/acquis.d/ point at the wrong paths. And decisions full of CAPI entries with zero crowdsec-origin ones means your box is consuming community intel while detecting nothing locally, which after a fresh install is normal for a few quiet hours and after months means something's off. The common CrowdSec errors cheat sheet covers the metric patterns in more depth than fits here.
Inspect the pf tables the bouncer maintains
Decisions only matter once they land in pf. The firewall bouncer maintains its own tables, and you can read them directly:
pfctl -t crowdsec-blacklists -T show | head
pfctl -t crowdsec-blacklists -T show | wc -l
pfctl -t crowdsec6-blacklists -T show | wc -l
The first table carries IPv4 bans, the second IPv6. Healthy numbers sit in the tens of thousands once the community blocklist has synced. Empty tables while cscli decisions list is full point you straight back at the bouncer sections above. If you're also feeding the firewall curated lists through an alias, the agentless method from my CrowdSec blocklists on OPNsense and pfSense guide, remember those live in a separate table named after the alias, so don't panic when the counts differ.
Scenario detections not firing on filterlog
The freebsd and opnsense collections install by default and bring the pf log parser with them, reading the filter log at /var/log/filter/latest.log. Two gotchas eat most "port scan scenario never fires" reports. First, pf only logs what rules tell it to log, so a default-deny rule with logging off produces a silent log and a blind parser. Second, since CrowdSec 1.6.3 the private RFC1918 ranges are whitelisted out of the box, so scanning your own firewall from the LAN proves nothing; the events get discarded at the enrich stage by design. Test from an outside address, or accept the shortcut of a manual two minute decision as described in the OPNsense CrowdSec allowlist guide, which also covers what to do when that whitelisting works against you.
Remove and reinstall the plugin cleanly
Some installs aren't worth archaeology. Mixed package versions after a failed upgrade, tainted collections you don't remember touching, half-applied settings, a config directory full of experiments; at some point a clean slate beats another evening of diffing. Back up /usr/local/etc/crowdsec/ first (your acquisition files and any custom whitelists live there), note your Console enrollment key, then remove os-crowdsec from the firmware plugins page and confirm the crowdsec packages went with it before reinstalling. Fifteen minutes, and you're back on a known-good baseline the plugin can maintain.
Run the LAPI on a separate Linux box
A closing thought for heavier setups. Nothing forces the whole stack onto the firewall, and the docs describe a remote LAPI mode where OPNsense keeps only the bouncer and a Linux machine elsewhere runs the engine and database. The firewall stays lean, upgrades stop threatening your detection history and the SQLite database moves off the box least suited to hosting it. Fair warning from the docs: in that mode the Overview tab shows stale machine and bouncer lists, since the plugin can't query a remote LAPI for them. If that trade appeals to you, start with the CrowdSec install guide for Debian for the engine side; the OPNsense end then shrinks to one yaml file and an API key, which is exactly as much CrowdSec as I want living on a firewall.

