Emergency server help: get in touch

CSF AlmaLinux 10: nftables and ipset Problems Fixed

Why CSF behaves differently on AlmaLinux 10 and other EL10 systems, how the iptables-nft compatibility layer changes rule handling, why ipset-backed blocklists fail, and the settings that keep LFD useful while you choose a longer-term fix.

Published Updated 6 min read

AlmaLinux 10 and the rest of the EL10 family finished the move that EL9 started: the legacy iptables kernel modules are gone, iptables is a thin front-end that translates into nftables, and ipset in particular no longer does what CSF expects. CSF was written against the old stack and, although every maintained fork now runs on EL10, the experience ranges from “works with caveats” to “silently not blocking”. This guide explains what changed, how to see it on your server and how to configure around it.

Applies to CSF on AlmaLinux 10 and other EL10 systems

Short answer: On EL10 iptables is a front-end to nftables and ipset no longer creates working sets, so with LF_IPSET = "1" CSF reports thousands of blocked addresses while the kernel drops none. Set LF_IPSET = "0", keep DENY_IP_LIMIT around 500, disable remote blocklists and run csf -ra; then prove a test deny appears in nft list ruleset. For large blocklists on a panel-free server use the Aetherinox fork’s nftables wrapper, or move to firewalld with fail2ban.

What the compatibility layer does and does not do

On EL9 and EL10, iptables is really iptables-nft. Each legacy rule CSF writes is translated into an nftables expression inside a table called ip filter. For plain rules that works fine, and a csf -r on EL10 produces a working chain. Confirm which flavour you are on:

iptables -V
alternatives --display iptables 2>/dev/null | head -3
nft list tables

You should see (nf_tables) in the version string and tables named ip filter, ip6 filter and possibly ip nat after CSF starts. If nft list tables is empty while CSF claims to be running, the rules never landed.

The part that does not translate is ipset. CSF uses ipset when LF_IPSET = "1", and the deny-list handling for large blocklists depends on it for performance. On EL10 the userspace ipset tool talks to a kernel interface that is either absent or wrapped in a way that returns success without creating a functional set, so CSF thinks it loaded thousands of addresses and the kernel drops none of them.

Symptoms

  • csf -r completes without errors, but a deliberately denied IP can still connect.
  • ipset list prints sets with zero members, or fails with a netlink error.
  • /var/log/lfd.log shows blocklist refresh messages with large counts that never match anything.
  • csf -g 203.0.113.10 reports the address in csf.deny but the chain lookup shows no rule.

Test it directly. Add a throwaway deny for an address you control, then look for it in nftables rather than trusting CSF’s own report:

csf -d 203.0.113.10 test
nft list ruleset | grep -c 203.0.113.10

A zero from the second command on a server where LF_IPSET = "1" is the confirmation.

The immediate workaround

Turn ipset off in CSF so every deny becomes an individual rule that the compat layer can translate:

sed -i 's/^LF_IPSET = .*/LF_IPSET = "0"/' /etc/csf/csf.conf
csf -ra

This works and blocks correctly, but the cost is scale. Each address becomes a rule, and a chain with tens of thousands of rules slows every packet decision. Keep csf.deny short and let DENY_IP_LIMIT do its job:

DENY_IP_LIMIT = "500"
DENY_TEMP_IP_LIMIT = "200"

With those limits and no remote blocklists (which you should have disabled anyway after CVE-2026-65639, see the hardening guide), the rule count stays manageable and LFD’s brute-force blocking keeps working normally.

Choose a fork that knows about nftables

The original CSF v15.00 and the cPanel fork both take the iptables-nft route described above. The Aetherinox csf-firewall project added a wrapper that can emit native nftables rules and handles sets through nft rather than ipset, which restores efficient large-list handling on EL10. On a server with no panel, or with CyberPanel or Webmin, that is the least disruptive fix. On cPanel, stay on the cPanel fork and accept the LF_IPSET = "0" trade-off, because mixing forks on a cPanel host breaks the RPM update path.

If you are building fresh EL10 servers and do not have a decade of csf.conf to preserve, it is worth stepping back and running firewalld with fail2ban, which are nftables-native and packaged by the distribution. Our firewalld and fail2ban tutorial walks through it on both AlmaLinux 9 and 10.

Do not run firewalld alongside CSF

Fresh AlmaLinux 10 installs enable firewalld, and on a cPanel or DirectAdmin server it must be disabled before CSF starts, otherwise both write to nftables and the result depends on which started last:

systemctl disable --now firewalld
systemctl mask firewalld
csf -ra

nft list tables should then show only CSF’s tables. If you see a table named inet firewalld, firewalld is still active.

Kernel module note

The compat layer needs nf_tables, nft_compat and the xt_* match modules loaded. On hardened hosts where kernel.modules_disabled = 1 was set early in boot (a sensible mitigation after the 2026 kernel LPEs, see our mitigation guide), CSF may fail to add a rule that needs a module not yet loaded. Load them explicitly before locking modules:

cat > /etc/modules-load.d/csf.conf <<'EOF'
nf_tables
nft_compat
xt_conntrack
xt_multiport
xt_limit
xt_recent
xt_state
EOF

Verify

After applying the workaround, prove the firewall blocks:

csf -d 203.0.113.10 test
nft list ruleset | grep -c 203.0.113.10
csf -dr 203.0.113.10
iptables -L DENYIN -n | wc -l

The grep count should be at least one while the deny is in place, and the DENYIN chain length should be sensible for your DENY_IP_LIMIT. Watch /var/log/lfd.log for a day and confirm that brute-force blocks appear and, more importantly, that the addresses stop showing up in /var/log/secure afterwards.

A common pitfall is copying a csf.conf from an EL8 server, where LF_IPSET = "1" and long blocklists were fine, and never noticing that EL10 quietly stopped enforcing them. Add the nftables grep to your post-provisioning checklist for every new EL10 host.

CSF AlmaLinux 10 at a glance

CSF AlmaLinux 10 summary card: On EL10 iptables is a front-end to nftables and ipset no longer creates working sets, so with LF_IPSET = "1" CSF…
In short: On EL10 iptables is a front-end to nftables and ipset no longer creates working sets, so with LF_IPSET = “1” CSF reports thousands of blocked addresses while the kernel drops none.

Official documentation: AlmaLinux wiki, Linux man pages.

Related guides: CrowdSec vs Imunify360 vs BitNinja: choosing a post-CSF security stack for shared hosting · Harden SSH on AlmaLinux 9 and Rocky 9 in 15 minutes · KernelCare on cPanel and DirectAdmin servers: setup, verification and rollback.

Frequently asked questions

Does CSF work at all on AlmaLinux 10?

Yes, through the iptables-nft compatibility layer, and plain rules and LFD brute-force blocks land correctly; the failure is specific to ipset-backed lists, which appear loaded but block nothing until LF_IPSET is turned off.

How long does the LF_IPSET workaround take to apply?

A one-line change to csf.conf and csf -ra takes under a minute, with a few seconds of rebuilt chains; the lasting cost is that each denied address becomes an individual rule, which is why the deny limits must stay small.

Can I keep firewalld running next to CSF on a fresh AlmaLinux 10 install?

No. Both write to nftables and whichever starts last wins, so disable and mask firewalld before starting CSF and confirm nft list tables shows no inet firewalld table.

Maintenance record

This guide changes servers, data or security settings, so we re-check it against current versions on a fixed schedule. Take a backup or snapshot before you start.

Maintained by
srvScripts editorial team
Supported versions
CSF on AlmaLinux 10 and other EL10 systems
Last full review
Next review

Free website test

Is your website set up right?

Check SSL, security headers, redirects, robots.txt, sitemap, llms.txt and security.txt in one test. It takes about 30 seconds.