Home NIDS using Raspberry Pi 5
This is a project that has been well documented all over the internet. Lots of people enjoy building their own Intrusion Detection/Prevention System for their home network and many choose to use a Raspberry Pi. The idea occurred to me as a colleague and I were discussing his home-built router using OpnSense. Fortunately, great minds think alike, and I was able to find a LOT of stories and examples online of how to build a NIDS.
My main inspiration came from this Reddit post. While the versions are slightly dated the premise is the same. I’ll provide a step by step here of what I did not only to help you, but also so I don’t forget the next time I need to troubleshoot this, or build another.
Hardware
- Raspberry Pi 5 (2.4GHz quad-core 64-bit Arm Cortex-A76 CPU, 8GB RAM) – $89.99
- Sandisk 256GB Class 10 A1 micro SD card (overkill, I know, can get away with using 32GB) – $21.99
- Case with cooling fan – $12.99
- 10″ touchscreen (optional) – $52.49
- HDMI to microHDMI cable (optional if you are getting the screen) – $6.99
- 5VDC power supply and USB-C cable for power (I have too many of these lying around the house)
OS
I went with Raspberry Pi Lite 64-bit. Unless you plan on dual purposing this device you don’t “need” a GUI. Command line is good enough considering it’s going to sit there and monitor/report on traffic.
You can use the Raspberry Pi Imager tool to quickly and accurately format and install a new SD card with the OS of your choice.
Network configuration
Network: For the purposes of installing and configuring the myriad of modules and packages you will be downloading, you’ll need to decide if you want to use Wi-Fi or a wired network. If you choose Wi-Fi, you’ll need to do the following first:
nmcli radio wifi on
For me, the radio was disabled. So when I ran raspi-config and nothing was there, I had to go back and actually turn it on. Then run raspi-config to connect to your Wi-Fi network.
Sudo: I acknowledge there are ways to add a user to the sudoers group, etc. You configure your device’s security in whichever way works best for you. Since this device is physically plugged into my network in my home in a secured location, I’m ok with running the following command for all of the remaining commands below
sudo -i su
This effectively elevates you to root for this session, eliminating the need to type sudo before every command. This is not recommended for regular use in Linux. It is ultimately your choice. It’s not hard to perma-elevate yourself then accidentally wipe the disk you’re working on, for example. Use caution and pay attention!
If there are any other customizations you want to make do those first. For example, if you’re using a screen and you don’t want it to auto-blank (that’s a default now), make sure you turn that off in raspi-config. Also if you want to auto-login on startup/restart, don’t forget to set that in raspi-config as well.
Suricata
I chose Suricata as my IDS platform, because Snort didn’t have a package that would work on ARM processors. They’re all good in my opinion, and Snort was my first thought. But so far Suricata is just as good and highly customizable.
This will install all of the dependencies needed for the Suricata install.
sudo apt -y install autoconf automake build-essential cargo cbindgen libjansson-dev libpcap-dev libpcre2-dev libtool libyaml-dev make pkg-config rustc zlib1g-dev
This will download Suricata. At the time of this post 8.0 was the latest version. Please make sure you’re getting the latest version in the future.
wget https://www.openinfosecfoundation.org/download/suricata-8.0.0.tar.gz
Extract the compressed download and switch directories so you’re in the folder you just extracted
tar -xvf suricata-8.0.0.tar.gz
cd suricata-8.0.0
Run the configure script, passing in the following arguments
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=/var --enable-nfqueue --enable-lua
Run the following to build and compile
make
make-install
Assuming there are no errors (and believe me there were PLENTY along the way for me) you now have Suricata installed! Here are some of the pitfalls I encountered:
Wrong version of Rust. This required me to uninstall Rust, install Rustup and let it install the latest version (at the time of this post, it was 1.88)
sudo apt remove rustc
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
. "$HOME/.cargo"
Permission issues. Many guides will tell you to create a user, add the user to a group, then give the group access to the Suricata folder. There’s a lot more than that, and when I tried to do this I ended up needing to wipe the SD card and start over – twice. Most likely my inexperience with Linux, but exercise caution anyway.
Update Rules
Suricata uses a rules file to compare traffic against and determine if there’s a threat. This file should have updated on its own when you ran the installer, but for good measure I ran it again
sudo suricata-update
Create a service
Next I created a service to run Suricata for me, that way I can ensure if the device restarts it will start on its own. Start off by creating the file and opening the editor
sudo nano /etc/systemd/system/suricata.service
Once in the editor add the following
[Unit]
Description=Suricata Intrusion Detection Service
After=network.target syslog.target
[Service]ExecStart=/usr/bin/suricata -c /etc/suricata/suricata.yaml -i eth0 -S /var/lib/suricata/rules/suricata.rules
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill $MAINPID
[Install]
WantedBy=multi-user.target
At this point, you should be able to start the service and ensure it’s running
sudo systemctl start suricata.service
sudo systemctl status suricata.service
It takes around 20-30 seconds to get going, so run the status line after a short bit to give it time. If you see “running”, you’re good! If not, the errors should tell you what needs to be addressed.
The service will now start automatically anytime the Pi reboots!
Logs and log rotation
Suricata’s log files can get very big very fast, depending on your traffic in and out of your home network. It’s recommended to not keep logging on the same disk as the OS
- Those SD cards are great, but they can’t take a beating like an SSD or NAS is designed to, with constant I/O and read/writes.
- The main SD card has the OS on it! Protect that at all costs! Leave plenty of room on there for updates, upgrades, general memory usage, etc.
I had a Sandisk 120GB SSD among the parts I’ve collected over many years, so I got an adapter that converts it to USB, so I could connect it to the Pi and mount it to the OS for log storage. 120GB should be plenty to handle long-term storage.
Mounting an external drive
To get the Pi to recognize this as attached storage, first create a directory for the drive (example)
sudo mkdir /mnt/exdisk
Now find the external drive you plugged in
sudo fdisk -l
You’re looking for the drive that starts with /dev and has the size you know should be there. Depending on how you formatted the drive there could be more than one volume present, so you may see something like this

In this case I wanted /dev/sda2, because that’s where my main partition and volume were. Now it’s time to mount the disk to the directory you just created
sudo mount /dev/sdb2 /mnt/exdisk
That’s it! However, you’ve successfully mounted it for this session. To make the drive auto-mount at every reboot, there’s a few more steps. We’re going to start with getting the UUID of the drive
sudo blkid
You’re looking for the line with your Device UUID, like so (not the PARTUUID, just the UUID)

Next, edit your fstab file like so, adding the line below the command to an empty row in the file (just make sure you use your UUID and directory you made when you mounted the drive above)
sudo nano /etc/fstab
UUID=D447-6260 /mnt/exdisk fstype defaults,auto,users,rw,nofail 0 0
That’s it, you should now be able to type sudo mount -a and get no errors!
Updating your log path
Depending on how you configure Suricata, by default there are four main log files:
- suricata.log – this populates anytime you’re starting/stopping the application
- fast.log – this is your alerts log
- eve.json – this is all of your events and traffic…everything
- stats.log – this is a point-in-time summary (again, depending on how you configure Suricata) of your network. Packets, drops, etc.
All three except suricata.log can fill up quick and take up a good deal of space depending on your network traffic. You can change where Suricata writes log files with the following
sudo nano /etc/suricata/suricata.yaml
Look for the line that says “default-log-dir” and you can point to your new external drive path. You’ll need to restart the service you created if you created one.
Log Rotation
You can set things up so your log files are compressed regularly and only keep n number of days’ worth of log files. There’s a lot of ways to customize this, I’ll share what I chose to do. I wanted to keep 7 days’ worth of all log files and compress them after each day. To do this, start out by creating a logrotate file just for Suricata
sudo nano /etc/logrotate.d/suricata
Put the following into the file and save (make sure you update your log path to whatever you set yours to above)
/mnt/exdisk/suricata/*.log {
rotate 7
daily
missingok
compress
delaycompress
notifempty
sharedscripts
postrotate
/usr/bin/systemctl reload suricata > /dev/null 2>&1 || true
endscript
}
/mnt/exdisk/suricata/eve.json {
rotate 7
daily
compress
missingok
notifempty
sharedscripts
postrotate
/usr/bin/systemctl reload suricata > /dev/null 2>&1 || true
endscript
}
This will effectively take the current log files, compress them, rename them by appending an incremental digit to the end of the filename, not worry if it’s in the middle of writing (not a big deal to me), run at midnight each day, only keep 7 days of log files, don’t run if the log file is empty, and after the rotation restart the service. Very simple yet thorough!
Optional: Push log files to cloud storage
For redundancy and quick visibility, I wanted to copy my log files up to my OneDrive, more of a quick cloud backup but also for doing some analysis and reporting later on. This was easily accomplished by using rclone. Installation is simple:
sudo apt install rclone
Then just follow the instructions to set up a remote connection. Here’s the guide for OneDrive. Note if you chose the headless install of Raspberry Pi (Debian Bookworm Lite) you’ll need to install rclone on another machine in order to authenticate. It is possible to download rclone to Windows, ru nthe command-line file, authenticate and have it give you a token you then copy/paste into your RPi when you’re setting up the remote connection.
To regularly copy these files up, just add an entry to CRON like so. This will run every hour and copy the contents of the log path to my OneDrive/Documents/Network/Suricata folder. The parm “–local-no-check-updated” ensures if data is being written to the file at the time to copy anyway. Omitting this parm could cause the command to break with “access denied”.
0 * * * * sudo rclone copy /mnt/exdisk/suricata OneDrive:Documents/Network/Suricata --local-no-check-updated
Notifications
The last thing I wanted to do was somehow get notifications when a new alert was generated. I explored sending email and leveraging an email-SMS gateway, but Verizon stopped providing an email-SMS gateway for non-business customers years ago. Fortunately there are plenty of open-source SMS gateways available. So I settled on using textbee.dev. This free solution provides what amounts to a workaround SMS gateway by turning your phone into the gateway. You install an app on the phone, register it with their API, then make API calls and the app will send an SMS text on your behalf. Pretty smart!
Ok, so the steps I needed to configure monitoring and notifications was
- Establish a means of notification (textbee)
- Build a script that checks the fast.log file and calls the API if needed
- Add the script to cron so it runs regularly
Since we covered the SMS gateway, next is the script to monitor the log file. Suricata has hundreds of rules and you can add your own if you like, too. I commented some out that were adding bloat to my logs that I wasn’t concerned about. I decided basically I wanted to know about moderate or high threats. Suricata classifies these as 1 or 2. You can get the list of classifications in /etc/suricata/classification.config. So to get the list of high threats I ran the following
sudo grep -e ",1" /etc/suricata/classification.config
Each row has a comma-separated series of values, the middle has the value that gets written to fast.log. Example:
not-suspicious,Not Suspicious Traffic,3
Keep these values, you’ll need them for the next step.
Next I wrote the Python script that checks the path and calls the API. Note recently the Raspberry Pi OS releases have prevented installing/using pip and related libraries, citing “externally managed environment”, so you may need to create a virtual environment to work out of. Here’s an example:
python -m venv ~/.env
source ~/.env/bin/activate
I created the script file and added the following:
from collections import deque
import sys
import requests
search_strings = ["value1","value2","etc"]
with open("/mnt/exdisk/suricata/fast.log") as log:
line_group = deque(log, 40) # number of lines at end of file you want to check
for line in line_group:
for string_item in search_strings:
if string_item in line:
payload = {"recipients": [ "+1XXXXXXXXXX" ],"message":f"{string_item}"}
headers = {"x-api-key":"your-api-key-goes-here","Content-Type":"application/json"}
r = requests.post("https://api.textbee.dev/api/v1/gateway/devices/{device-id}/send-sms", json=payload, headers=headers)
print(r)
sys.exit(0) # don't send in a loop
Make sure you update the path to your fast.log file, also your textbee API key and device ID. Now we need a way to run this regularly to check the log file. That’s easily accomplished with an entry in CRON (just update the path to your script file and put in your script file name).
*/15 * * * * /path/venv/scriptfile.py
And there you have it. A fully functional IDS on my home network that keeps 7 days of logs and notifies me by text message when a threat is detected! I’ll be doing more with this, including doing some Power BI analysis and adding on an IPS solution to drop threats and notify the intended recipient of the attempted threat. For now, I’m happy it works!
Feel free to share other ideas, maybe things that worked for you, better ways of doing things, etc. I’m always open to new ideas and different perspectives.