Snort is a network intrusion detection and prevention system maintained by Cisco Talos. Snort 3 is a full rewrite of the classic engine with a Lua configuration, multithreaded packet processing and a new rule syntax. Ubuntu 24.04 only packages the older Snort 2.9, so in this tutorial you will build Snort 3 and its packet acquisition library (LibDAQ) from source, configure your network, load the free community ruleset and a custom rule, write alerts in JSON and run Snort as a systemd service in IDS mode.
Prerequisites
To follow this tutorial you need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 2 CPU cores and 4 GB of RAM. Compiling Snort is memory hungry; with 2 GB it works but you should add swap and run
makewith fewer jobs. - About 5 GB of free disk space for the sources and build.
- A non-root user with
sudoprivileges. - A second machine to generate test traffic (ping) against the server.
Step 1 - Installing build dependencies
Snort 3 needs a C++17 compiler, CMake and several libraries: libpcap for capture, libdnet, hwloc, LuaJIT for the configuration, OpenSSL, PCRE2 and zlib. LibDAQ additionally needs the autotools. Install everything from the Ubuntu repositories:
sudo apt update
sudo apt install build-essential cmake pkg-config git autoconf automake libtool flex bison \
libpcap-dev libpcre2-dev libdumbnet-dev libhwloc-dev libluajit-5.1-dev libssl-dev \
zlib1g-dev liblzma-dev uuid-dev libunwind-dev ethtool
Create a working directory for the sources:
mkdir -p ~/snort-src
cd ~/snort-src
Step 2 - Building and installing LibDAQ
The Data Acquisition library (LibDAQ) is the layer between Snort and the capture method (pcap, AF_PACKET, NFQ). Snort 3 requires LibDAQ 3.x, which is also not packaged for Ubuntu. Clone it from the official Snort 3 GitHub organization and build it:
git clone https://github.com/snort3/libdaq.git
cd libdaq
./bootstrap
./configure
make
sudo make install
The library is installed under /usr/local/lib. Refresh the dynamic linker cache so Snort can find it:
sudo ldconfig
Check that the DAQ modules were built. You should see afpacket and pcap among them:
ls /usr/local/lib/daq/
daq_afpacket.la daq_afpacket.so daq_bpf.la daq_bpf.so daq_dump.la daq_dump.so daq_fst.la daq_fst.so daq_nfq.la daq_nfq.so daq_pcap.la daq_pcap.so ...
Step 3 - Building and installing Snort 3
Go back to the working directory and clone Snort 3. The default branch of the repository always points to the latest release:
cd ~/snort-src
git clone https://github.com/snort3/snort3.git
cd snort3
Configure the build with /usr/local as the prefix, then compile it. The compilation takes 10 to 30 minutes depending on the CPU:
./configure_cmake.sh --prefix=/usr/local
cd build
make -j"$(nproc)"
sudo make install
sudo ldconfig
At the end of the configure step CMake prints a feature summary. If a required library is missing it stops with an error that names the package, so install it and run the command again.
Verify the installation:
snort -V
,,_ -*> Snort++ <*-
o" )~ Version 3.x.x.x
'''' By Martin Roesch & The Snort Team
http://snort.org/contact#team
Copyright (C) 2014-2026 Cisco and/or its affiliates. All rights reserved.
...
Using DAQ version 3.0.x
Using LuaJIT version 2.1.x
Using OpenSSL 3.0.13 30 Jan 2024
Using libpcap version 1.10.4 (with TPACKET_V3)
Using PCRE2 version 10.42 2022-12-11
Using ZLIB version 1.3
The default configuration files were installed in /usr/local/etc/snort/. Validate them before changing anything:
snort -c /usr/local/etc/snort/snort.lua -T
...
Snort successfully validated the configuration (with 0 warnings).
o")~ Snort exiting
Step 4 - Preparing the network interface
Modern network cards and virtio NICs merge small packets into large ones before the kernel sees them (Generic Receive Offload and Large Receive Offload). Snort then sees packets larger than the real wire size, which breaks stream reassembly. Find your interface name:
ip -brief address show
lo UNKNOWN 127.0.0.1/8 ::1/128
eth0 UP 203.0.113.25/24 fe80::1/64
In the rest of this guide replace your_interface with that name (here eth0) and your_server_ip with the server address. Disable GRO and LRO on the interface:
sudo ethtool -K your_interface gro off lro off
Check the result:
ethtool -k your_interface | grep receive-offload
generic-receive-offload: off
large-receive-offload: off [fixed]
This setting does not survive a reboot. The systemd unit you create in Step 8 applies it every time Snort starts.
Step 5 - Configuring HOME_NET and outputs
Open the main configuration file:
sudo nano /usr/local/etc/snort/snort.lua
At the top of the file, in section 1, set HOME_NET to the addresses you want to protect and EXTERNAL_NET to everything else:
HOME_NET = 'your_server_ip/32'
EXTERNAL_NET = '!$HOME_NET'
Scroll down to section 7, "configure outputs". Enable the JSON logger by adding this block. With file = true, Snort writes alert_json.txt into its log directory instead of printing to the console:
alert_json =
{
file = true,
limit = 100,
fields = 'timestamp msg sid src_addr src_port dst_addr dst_port proto action',
}
The limit value is in megabytes. When the file reaches that size Snort rotates it.
Save the file and validate the configuration again with snort -c /usr/local/etc/snort/snort.lua -T.
Step 6 - Adding the community rules and a local rule
Snort 3 does not ship with rules. Talos publishes a free community ruleset in Snort 3 format. Create a directory for rules and download it:
sudo mkdir -p /usr/local/etc/rules
cd ~/snort-src
curl -fLO https://www.snort.org/downloads/community/snort3-community-rules.tar.gz
sudo tar -xzf snort3-community-rules.tar.gz -C /usr/local/etc/rules/
ls /usr/local/etc/rules/snort3-community-rules/
AUTHORS LICENSE VRT-License.txt sid-msg.map snort3-community.rules
Next, create a file for your own rules:
sudo nano /usr/local/etc/rules/local.rules
Add a rule that fires on every ICMP echo request sent to the server. It is intentionally noisy and only meant for testing:
alert icmp any any -> $HOME_NET any ( msg:"LOCAL ICMP echo request to server"; itype:8; sid:1000001; rev:1; )
Use signature IDs of 1000000 and higher for local rules.
Tell Snort to load both files. Open snort.lua again and find the ips block in section 5. Replace it with:
ips =
{
enable_builtin_rules = true,
variables = default_variables,
rules = [[
include /usr/local/etc/rules/local.rules
include /usr/local/etc/rules/snort3-community-rules/snort3-community.rules
]],
}
enable_builtin_rules turns on the decoder and inspector alerts (malformed packets, protocol anomalies), which are useful in IDS mode. Validate the configuration:
snort -c /usr/local/etc/snort/snort.lua -T
The output now includes a rule count summary:
--------------------------------------------------
rule counts
total rules loaded: 4200
text rules: 3800
builtin rules: 400
option chains: 4200
chain headers: 330
...
Snort successfully validated the configuration (with 0 warnings).
Step 7 - Testing detection in the foreground
Before creating a service, run Snort in the foreground and print alerts to the console. -A alert_fast selects the one-line text logger, which prints to the terminal because you did not configure it with file = true; -k none skips checksum validation (virtual NICs often deliver packets with offloaded checksums) and -s 65535 captures full packets:
sudo snort -c /usr/local/etc/snort/snort.lua -i your_interface -A alert_fast -s 65535 -k none
From your second machine, ping the server:
ping -c 3 your_server_ip
Snort prints one alert per echo request:
09/25-10:31:12.482901 [**] [1:1000001:1] "LOCAL ICMP echo request to server" [**] [Priority: 0] {ICMP} 198.51.100.7 -> 203.0.113.25
09/25-10:31:13.484112 [**] [1:1000001:1] "LOCAL ICMP echo request to server" [**] [Priority: 0] {ICMP} 198.51.100.7 -> 203.0.113.25
Press Ctrl+C to stop Snort. It prints packet and alert statistics on exit.
Step 8 - Running Snort as a systemd service
Running Snort as root is not needed after it opens the interface. Create a system user and a log directory owned by it:
sudo useradd --system --no-create-home --shell /usr/sbin/nologin snort
sudo mkdir -p /var/log/snort
sudo chown snort:snort /var/log/snort
sudo chmod 750 /var/log/snort
Create the unit file:
sudo nano /etc/systemd/system/snort3.service
[Unit]
Description=Snort 3 network intrusion detection
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStartPre=-/usr/sbin/ethtool -K your_interface gro off lro off
ExecStart=/usr/local/bin/snort -c /usr/local/etc/snort/snort.lua -i your_interface -s 65535 -k none -l /var/log/snort -m 0x1b -u snort -g snort
Restart=on-failure
[Install]
WantedBy=multi-user.target
The leading - in ExecStartPre lets the service start even if the NIC does not support one of the offload settings. -l sets the log directory, -m 0x1b sets the file creation mask, and -u/-g drop privileges to the snort user once capture has started.
Start the service and check it:
sudo systemctl daemon-reload
sudo systemctl enable --now snort3
sudo systemctl status snort3
● snort3.service - Snort 3 network intrusion detection
Loaded: loaded (/etc/systemd/system/snort3.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-25 10:35:02 UTC; 8s ago
...
Ping the server again from the second machine, then read the JSON log with jq (install it with sudo apt install jq if needed):
sudo tail -n 1 /var/log/snort/alert_json.txt | jq .
{
"timestamp": "09/25-10:36:40.118102",
"msg": "LOCAL ICMP echo request to server",
"sid": 1000001,
"src_addr": "198.51.100.7",
"dst_addr": "203.0.113.25",
"proto": "ICMP",
"action": "allow"
}
action is allow because Snort runs passively and only reports. Once you have confirmed that detection works, comment out the ICMP test rule in local.rules with # and restart the service with sudo systemctl restart snort3.
Step 9 - Suppressing noisy rules
Some rules will fire on legitimate traffic in your environment. Rather than editing the community rules file, which you will replace on the next download, add a suppress block to snort.lua. Section 8, "configure tweaks", or the end of the file are good places:
suppress =
{
-- ignore a rule everywhere
{ gid = 1, sid = 2000419 },
-- ignore a rule only for one source address
{ gid = 1, sid = 1000001, track = 'by_src', ip = '198.51.100.7' },
}
Replace the SIDs with those of the rules you want to silence. Validate with snort -c /usr/local/etc/snort/snort.lua -T and restart the service.
To see which rules fire most often, count the sid and msg fields in the JSON log:
sudo jq -r '"\(.sid) \(.msg)"' /var/log/snort/alert_json.txt | sort | uniq -c | sort -rn | head
Troubleshooting
snort: error while loading shared libraries: libdaq.so.3. Runsudo ldconfigafter installing LibDAQ. If it persists, confirm that/usr/local/libis listed in/etc/ld.so.conf.d/libc.conf.Can't find daq module afpacketorpcap. LibDAQ was built without the module or installed to another prefix. Rebuild LibDAQ and checkls /usr/local/lib/daq/.- The service fails right after starting. Read the error with
sudo journalctl -u snort3 -n 30 --no-pager. The most common cause is a typo insnort.lua, whichsnort -c /usr/local/etc/snort/snort.lua -Tpoints out with a line number. makeis killed during compilation. The build ran out of memory. Add swap or runmake -j1.
Conclusion
You built Snort 3 and LibDAQ from source on Ubuntu 24.04, configured your home network, loaded the community ruleset and a local rule, and are now running Snort as a hardened systemd service that writes alerts in JSON. As next steps, automate rule downloads with PulledPork 3 (and a free Snort.org Oinkcode for the registered ruleset), forward alert_json.txt to your log platform, or run Snort inline with the nfq DAQ module to block traffic instead of only reporting it.
