Raspberry Pi Network Monitor: Free Dashboard for Home Internet Traffic

Raspberry Pi Network Monitor: Free Dashboard for Home Internet Traffic

traffic monitoring software with prometheus and grafana

When trying to figure out why the internet is slow, it can be hard to learn exactly which device on the network is eating up all the bandwidth. Many solutions to this problem require software to be installed on every device to be monitored. Instead, I tried to build a custom Raspberry Pi network monitor.

This post will show you how to monitor all internet traffic for every device on your network, without buying any specialty hardware.

This open-source solution has been used by readers of this site for monitoring family internet usage, LAN parties, and more.

This project came about when retrofitting our cabin in the woods to a smart-home. There internet providers out here advertise about 30 Mbps (down) and 2 Mbps (up). This isn’t much to work with. Plus, we have many home-made IOT devices scattered around the house. So… who knows where the traffic is going?

My goal was to not require any special software, yet monitor the internet traffic for every network device.

Ideally, I also wanted to create a beautiful Grafana dashboard. This would let me see what sites the devices were contacting with the Raspberry Pi home network monitor.

It seemed like a couple others on Reddit were also interested in a solution to this problem, so I decided to give it a try. The result was an open-source Python script / Docker container, meant to be run on a Raspberry Pi, that exports data to Prometheus. While I used a Raspberry Pi, the code should run on any Linux distribution. There are Docker images for both arm and amd.

Skip to the end of this post for source code & installation.

But first, let’s consider the different ways to monitor traffic on a home network…

Internet Traffic Monitor: Approaches

Based upon experience and some research, these are the possibilities I came up with:

  1. Pi as a router 
    The obvious way to monitor network traffic. The Raspberry Pi sits between the devices to be tracked and the internet (e.g., acting as a router or access point). Unfortunately, this can slow down the network, which causes many to avoid the approach (see the next section).
  2. Router reporting 
    Some modern routers provide features along these lines. But generally custom firmware is required.
  3. Device reporting 
    The standard protocol for this is SNMP, which will rely upon device side installations to self-report. It integrates well with Prometheus/Grafana though.
  4. Packet sniffing
    You could theoretically monitor the wireless traffic (if all you care about is WiFi). This is the same concept that allows attackers to sniff traffic on a WiFi network.

Each of these has its drawbacks. I did not want to buy a new router, so router reporting was not an option. I could not install the necessary software on all the IOT devices, which prevents device reporting. And packet sniffing is an interesting idea, but I wanted to be able to handle wired as well as wireless traffic.

This left only one approach: a Raspberry Pi network monitor.

Raspberry Pi Home Network Monitor

If all internet traffic is going to pass through a device, it is good to use caution.

The first concern is that of security. I won’t say too much about that here, except to mention that a firewall of some kind is a good idea. I went with Uncomplicated Fire Wall (ufw) because it is, well, uncomplicated.

A less obvious concern is that of speed. When traffic passes through a router/switch, the primary bottleneck is the ethernet hardware. In other words, the CPU and RAM are not as important as in other cases. This was something of a problem with the Raspberry Pi 3B (and lower). However, the Raspberry Pi model 4 has an upgraded on-board 1000 Mbps eth0 port.

Make sure that the ethernet hardware meets the needs.

Failure to do so could slow down the entire network!

With that in mind, here is the exact list of parts I used.

If you're new to Raspberry Pi, the popular CanaKits are a great place to start. I prefer to buy the Raspberry Pi 4, power adapter, micro SD cards, and heatsinks separately. Not only is this cheaper, but it reduces e-waste.

The following are affiliate links to other parts I used in this project. I never link to a product that I have not personally used.

With the parts in-hand, I drew up this Raspberry Pi 4 router design:

raspberry pi network monitor traffic diagram
This router to switch connection diagram shows how we monitor network traffic.

If you’re using the above parts list…

Some important points:

  • The WiFi router is in Bridge mode. This means that eth0 must act as the DHCP server (assign IP addresses to the network).
  • Traffic between devices on the network will not flow through the Raspberry Pi. See the Performance Tests, below.
  • This means that the Pi is only a bottleneck for internet traffic. With 1000 Mbps hardware and an ISP that only provides 30 Mbps, we won’t be hitting this limit any time soon.

There are many ways to set up the eth1 <> eth0 connection. You could configure this using internet bonding software. This would let you add another internet connection (eth2) to make the internet connection even faster. For a more complete DIY Raspbery Pi router solution:

Alternate Design

If you’re not using the Raspberry Pi as a router, this section is for you.

The traffic must flow through the device.

You must have two network interfaces over which the traffic you wish to capture passes.

You could flip the WiFi router and Rasbperry Pi from the above diagram. This approach can work better if you prefer to not use the Raspberry Pi as a router:

  • The WiFi router connects to the modem/internet (not in bridge mode).
  • The Raspberry Pi connects to the internet through the WiFi router.
  • The Raspberry Pi should have a static IP assigned by your WiFi router (see its documentation).

However, it does have one major disadvantage: the WiFi traffic (going to the router) will not be monitored. But the major advantage is: if you ever want to remove the Raspberry Pi network monitor, just plug the WiFi router directly in to the switch.

You could also run a separate DHCP server on the WAN side of the Raspberry Pi. In this case, again, the Pi is not the router. However, if the two network interfaces are bridges, then the traffic is flowing through the Pi.

No matter the design, the device acting as the router connects to the internet, and the device connected to the switch is in bridge mode. In other words, you must manually bridge the two interfaces on the Raspberry Pi. Therefore, the Pi’s eth0 is able to see traffic passing in and out of the LAN.

Performance Tests

After implementing the Pi as a router, I saw no decrease in speed for intra-network traffic. This was tested with iperf3. It showed:

  • ~910 Mbits/sec for two computers connected via a physical switch.
  • ~180 Mbits/sec when separated by a long WiFi hop.
Our ISP only advertises 30 Mbps! We never saw speeds above that before implementing the Pi as a router.

Shockingly, I saw improved external (internet) speeds with the Raspberry Pi network monitor. I already had Node RED running a speedtest every 5 minutes and recording the data to Home Assistant + Prometheus. When using the CenturyLink provided DSL router, I rarely saw speeds above 25 Mbps (down). Now we consistently seeing speeds in the ~33 Mbps range. This is likely because the Raspberry Pi is using pppoeconf to establish the DSL connection directly, and it does a better job managing this connection than the modem provided by the ISP.

Accuracy Tests

Now, to test the accuracy of the Raspberry Pi network monitor.

Using Prometheus for throughput/bandwidth will not be perfectly accurate on a short time scale. This is due to the way a rate is averaged over an interval. However, by downloading a large file, I was able to compare the reported download speed from Chrome with that of the traffic graph:

raspberry pi network monitor dashboard with Grafana
The ~2.4 MB/s reported by Chrome matches the rate reported by Grafana, albeit with some reporting-time lag.

In addition, the total download size matched that reported by Chrome:

Raspberry Pi network monitor dashboard downloads by server
Note: this graph is computed via an increase on a counter. If the monitoring software gets reset, the values will not be accurate. If anybody knows a better way to do this with PromQL, please do let me know.

Screenshots, Installation, & Source Code

This project is open-source. It is available as a Python script or Docker image.

Find the code & detailed documentation for the Raspberry Pi Network Monitor in the network-traffic-metrics Github repository.

The most important part of the configuration is setting up the tcpdump filters. For example, the following will restrict the captured traffic to that which flows in or out of the 192.168.0.0/24 subnet:

(src net 192.168.0.0/24 and not dst net 192.168.0.0/24) or (dst net 192.168.0.0/24 and not src net 192.168.0.0/24)

For more help setting up the filters, check out this blog post by Daniel Miessler on isolating traffic with tcpdump. If you’re having trouble with anything else, please comment below or contact me directly.

Step-by-Step + Configuration File

I’ll try to give copy and paste instructions below. However, as I mentioned in the “Using the Pi as a Router” section, some of these steps may be highly individual. The bridge steps, in particular, can depend on the exact Linux version you have installed. I’ll assume you…

  • Have eth0 (LAN) and eth1 (WAN) on your device.
  • Pi will reside at 192.168.0.1, either as the router or as a pass-through (alternate design).
  • IP addresses will be handed out on the 192.168.0.0/24 subnet.
  • You want to use Google’s DNS servers (8.8.8.8 and 8.8.4.4)
  • Running Raspbian Buster.

1. Build or Configure the Router

If you wist to use the Raspberry Pi as the router (first option), please see this article. If you wish for the WiFi router to connect to the internet (alternate design), follows its instruction manual to assign the Pi a static IP address (192.168.0.1 in this example).

2. Run the Raspberry Pi network monitor script

If you’re comfortable with it, a Docker/Kubernetes install may be easier than these manual steps. Otherwise…

sudo apt-get install git python3-pip tcpdump
sudo pip3 install argparse prometheus_client
git clone https://github.com/zaneclaes/network-traffic-metrics.git
cd ./network-traffic-metrics
sudo python3 ./network-traffic-metrics.py "(src net 192.168.0.0/24 and not dst net 192.168.0.0/24) or (dst net 192.168.0.0/24 and not src net 192.168.0.0/24)"

Open your web browser to http://192.168.0.1:8000/metrics to see the counters being exported for Prometheus. Verify that you’re seeing data that seems to match the traffic on your network. It may be hard to read, but upon refreshing the page you should see more lines added as people connect to different sites.

You can change several options by passing certain command-line flags to the script. For example, the script assumes that you want to listen to the eth0 interface. If you aliased this to the name lan, per the Raspberry Pi router guide, you could add the --interface lan flag. Or, the --port 80 flag would change from listening on port 8000 to port 80. Note that all the configuration variables may also be set via environment variables, like NTM_INTERFACE.

To make the script start on reboot, type sudo crontab -e and add:

@reboot python3 /home/pi/network-traffic-metrics/network-traffic-metrics.py "(src net 192.168.0.0/24 and not dst net 192.168.0.0/24) or (dst net 192.168.0.0/24 and not src net 192.168.0.0/24)" &

Alternatively, you could create a systemd service (generally preferred).

3. Install Prometheus

Prometheus and Grafana can be run anywhere on the same network. I’d recommend not running them on the same device doing the metric exporting. This prevents slowing down the machine. The following instructions are copied more or less exactly from the official Prometheus docs:

Download the latest release of Prometheus for your platform, then extract and run it:

tar xvfz prometheus-*.tar.gz
cd prometheus-*

Create prometheus.yaml (see comments):

global:
  scrape_interval:     15s # How frequently to report
  external_labels:
    monitor: 'network-traffic-metrics'

scrape_configs:
  - job_name: 'network-traffic-metrics'
    static_configs:
      - targets: ['192.168.0.1:8000'] # The Network Traffic Metrics IP/port

Run Prometheus: ./prometheus --config.file=prometheus.yml

Check that you can access Prometheus: localhost:9090/metrics (or wherever it is located).

4. Install Grafana

Again, the official docs are a good place to start (these are copied fairly directly):

sudo apt-get install -y apt-transport-https
sudo apt-get install -y software-properties-common wget
wget -q -O - https://packages.grafana.com/gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install grafana-enterprise

Open up http://localhost:3000/ to find Grafana. Add Prometheus as a data source:

  1. Open the side menu by clicking the Grafana icon in the top header.
  2. In the side menu under the Dashboards link you should find a link named Data Sources.
  3. Click the + Add data source button in the top header.
  4. Select Prometheus from the Type dropdown.
Internet traffic monitor data in Prometheus and Grafana
Verifying that Grafana can see the Prometheus data.

The default options should match your installation from above. If you’ve used containers or otherwise installed Prometheus differently, you will need to use the appropriate URL for the Prometheus server. For example, I used the URL http://prometheus-server with a Kubernetes helm deployment. If your data source is configured correctly, you should now be able to use the Explore section to see the data in Prometheus.

You could fiddle with this yourself and create a dashboard to your liking. Or, you could use my Raspberry Pi network monitor dashboard. Grafana has instructions for importing dashboards. In short, you use the + button on the left to Import dashboard 12619 (or the JSON file).

Import the internet traffic monitor dashboard.
Importing the dashboard from GrafanaLabs.

You should now have a working Raspberry Pi network monitor that can be accessed from Grafana.

Build Guides

Looking for even more detail?

Drop your email in the form below and you'll receive links to the individual build-guides and projects on this site, as well as updates with the newest projects.

... but this site has no paywalls. If you do choose to sign up for this mailing list I promise I'll keep the content worth your time.

Written by
(zane) / Technically Wizardry
Join the discussion

21 comments
  • Thank you for the tutorial!

    Can you please let me know what is wrong?
    However, I encounter following errors while running this step

    2.Run the Raspberry Pi network monitor script.
    pi@raspberrypi:~/Downloads/network-traffic-metrics $ sudo python ./network-traffic-metrics.py (src net 192.168.0.0/24 and not dst net 192.168.0.0/24)
    bash: syntax error near unexpected token `(‘

    My python is version 3.7
    pi@raspberrypi:~/Downloads/network-traffic-metrics $ python –version
    Python 3.7.0

    I have installed both argparse and prometheus_client

    pi@raspberrypi:~/Downloads/network-traffic-metrics $ pip3 install prometheus_client
    Looking in indexes: [link to pypi.org], [link to www.piwheels.org]
    Requirement already satisfied: prometheus_client in /home/pi/.local/lib/python3.7/site-packages (0.8.0)

    pi@raspberrypi:~/Downloads/network-traffic-metrics $ pip3 install argparse
    Looking in indexes: [link to pypi.org], [link to www.piwheels.org]
    Requirement already satisfied: argparse in /home/pi/.local/lib/python3.7/site-packages (1.4.0)

    • Hey, glad you enjoyed it! And sorry, this was my mistake — looks like the example got garbled when I formatted the code. You need quotation marks around the filters argument, so that the command is: sudo python ./network-traffic-metrics.py "(src net 192.168.0.0/24 and not dst net 192.168.0.0/24)". The post should be updated with the fix now, as well.

      In case you’re curious, it’s because the entire filter clause is a single argument into the python script. Without the quotes, bash is trying to parse the arguments itself before passing them to the script, which it does not know how to do.

      Hope that helps!

      • Thanks for the quick respnse! I understand what could have been the issue. However, I am hitting another error. Sorry. I’m really bad at regex, so no idea what’s wrong here.

        pi@raspberrypi:~/Downloads/network-traffic-metrics $ sudo python ./network-traffic-metrics.py “(src net 192.168.0.0/24 and not dst net 192.168.0.0/24)”
        File “./network-traffic-metrics.py”, line 29
        return f'(?P{pattern})’
        ^
        SyntaxError: invalid syntax

        • Ah. I suspect that you have both python2 and python3 installed. To check, try python --version. Whatever it says is the default Python version on your machine.

          If it is <=3, as I suspect, first make such that which python3 works. You should discover the location to the actual python3 executable. If that exists, the easiest “fix” is to replace sudo python ... with sudo python3 .... Another approach would be to make an alias from /usr/bin/python to python3 so that v3 becomes your default python environment. Or you could use a python version management tool. Which approach is right for you depends on your circumstance, but the first is probably the least hassle.

          • Thanks!
            Yes! Indeed. My python installations seems to be a mess. I will fix that first. Thanks again.

      • Thanks for the quick reply. However, I am getting another error now. Sorry.

        pi@raspberrypi:~/Downloads/network-traffic-metrics $ sudo python ./network-traffic-metrics.py “(src net 192.168.0.0/24 and not dst net 192.168.0.0/24)”
        File “./network-traffic-metrics.py”, line 29
        return f'(?P{pattern})’
        ^
        SyntaxError: invalid syntax

  • Nice guide. I’m trying a slightly different approach – using a docker for graphana and another for Prometheus.
    With that in mind, when creating the dashboard in the last step, the JSON file has the “DS_PROMETHEUS” that wont work. Is there any way to make this work with this docker setup? What do I have to change?

    Thanks

    • Thanks for saying so! FWIW, I do have one container for Grafana and one for Prometheus, just like you. The difference may be that I use Kubernetes to deploy the containers, not the Docker agent. The problem you’re experiencing suggests that you do not have Prometheus configured as a “Data Source” in Grafana, or that somehow the name of that datasource does not match the convention (DS_PROMETHEUS).

      Others have had small hiccups importing the dashboard as well. You prompted me to do a little research and add the dashboard to the GrafanaLabs shared dashboards website. You can follow the import instructions with the GUID 12619. Hopefully that works better! First time I’ve shared a Grafana dashboard publicly 🙂

  • Hi, this is probably perfect for what I’m looking for. The instructions seem good too. One question, I would probably do this for the non-wifi devices so keep my router as the internet connection. That has a 192.168.1.1 ip address and i’d like to keep the other devices on the same subnet. Does the pi HAVE to be 192.168.0.1 or could it be 192.168.1.2 meaning all other devices which have static IP addresses would not have to change?

    • Glad to hear it 🙂 In that case, what you describe is actually preferable. The subnet I used was for example purpose. If your Pi is behind the router, it’s easiest to keep it on the same subnet. To do so, you just want to bridge the traffic between the two interfaces without adding a DHCP server or anything of that nature. This links might help:
      Bridging eth0 and eth1. If that doesn’t work or you need more help lmk.

  • Hello and thank you so much for the guide! I’m running into a problem running the monitor script, and I’d appreciate your help.

    I’ve confirmed that pip3 installed prometheus_client, but when I run the script straight out of the box, I get [code]ModuleNotFoundError: No module named ‘prometheus_client'[/code] I tried to fix this by adding sys.path.append() on line 2 to the path pip3 gave for prometheus_client. That cleared up that error, but gave me this one:

    File “/usr/lib/python3.7/subprocess.py”, line 1522, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
    FileNotFoundError: [Errno 2] No such file or directory: ‘tcpdump’: ‘tcpdump’

    • Hey Mike,

      It sounds like you’re having Python environment problems. You may have multiple versions of Python3 installed and/or some strange symlinks, given that python3 couldn’t find the package installed by pip3. Specifically, it sounds like Python3 is not referencing the same import paths as pip3 is installing to. If forcing an absolute path worked on the import, great, though I do tend to worry this may be causing other problems down the line.

      The second problem suggests that you don’t have tcpdump installed on the machine, or that Python3 cannot find it. The latter would match with your prior problem. Namely, that the shell environment from which Python3 is being run is not resolving your user paths. For example, on my RPi, which tcpdump gives /usr/sbin/tcpdump. If that command also works for you, it suggests that you’re invoking Python3 in such a way that this requirement is not resolved in the same way it is from your shell. You could just edit line 71 of the script to invoke the absolute path to tcpdump, just like you did with the prior problem, but again I worry that your shell environment may continue to cause problems.

      Cheers! Hope that helps a little…
      – Z

      • Thanks for your help!

        For the Python issue, I tried defining PYTHONPATH in my .bashrc, but that didn’t help. So, for now I stuck with the edit on your script.

        The tcpdump issue was simply that it wasn’t installed. Easy fix.

        After I got those resolved, I started the script and received an error that eth0 couldn’t be found. Following the router guide, I had given eth0 the alias ‘lan’ and eth1 ‘wan’. So I updated the one instance of eth0 and now it seems to be running happily.

        “(src net 192.168.1.0/24 and not dst net 192.168.1.0/24) or (dst net 192.168.1.0/24 and not src net 192.168.1.0/24)”
        tcpdump: listening on lan, link-type EN10MB (Ethernet), capture size 262144 bytes
        [SKIP] 06:33:43.603149 IP

  • I love this post – this is EXACTLY what I have been looking for, for like six months.
    I’ve even converted one of my old machines to pfsense just to do this, but had to scrap it because it was a big waste, even as VM.

    Question: Would this conflict with PiHole? It is my DHCP provider at the moment.

    Also a problem, I am running this:
    sudo python3 ./network-traffic-metrics.py “(src net 192.168.1.0/24 and not dst net 192.168.1.0/24) or (dst net 192.168.1.0/24 and not src net 192.168.1.0/24)”
    and receiving this error “ModuleNotFoundError: No module named ‘prometheus_client'”
    I’ve tried installing pyEnv to fix this but no dice.

    Do you happen to have a docker container or yml with everything running? That would be much easier for people.

    • Hey! Thanks for saying so.

      I don’t see any reason this would conflict with PiHole. I run AdGuard Home myself these days, but I used to run PiHole. You should be able to substitute the DHCP server for PiHole’s.

      Your problem with prometheus_client being missing is likely because you need to use sudo pip3 install. If you install without the sudo, but then try to run sudo python3, you’ll be using a different environment.

      Please refer to the section of the README devoted to deploying via Docker. There’s also a section devoted to Kubernetes based deployment, which is what I use personally.

        • This is working now – The graphs are empty though in graphana.
          The question I have is why is it not doing a passthrough from eth0 (WAN) to eth1 (LAN)?
          I don’t think this was part of this guide. Do I need to manually bridge the interfaces?

          • You have already mentioned that you are not using the “Pi as a Router” guide (no DHCP server). Therefore, technically you fall under the “Alternate Design” header in the tutorial (which indicates you must bridge the two interfaces). This would also explain the lack of data in Grafana. FWIW, I’d recommend taking the time to stop at the Prometheus step and perform the recommended validations, and then go “Explore” the data in Grafana, so you understand what is actually going on.

            Note that in the Alt design section I mainly referred to a Wifi router. You need to reason about your network and ensure that the traffic you wish to capture is flowing through your Pi, one way or another. That’s the key. As long as that is true, then the script will capture the data across those two network interfaces. There are lots of ways to do this, and without analyzing a network diagram of your setup it is hard to be perfectly accurate in my descriptions =/

          • No worries you put me on the right track after all – I am still new to Prometheus and grafana so I’ll play around with them.
            Yes – regarding the network setup I’ve already done that and everything should be flowing from the PI once I am done with the bridging.
            VDSL Router (No Wifi) => PI => Switch => Rest of the network.
            So nothing will connect to the internet without hitting the PI first.

            Was unable to reply to your last comment for some reason so I put this here.

            Many thanks for your support so far.