When you start exploring raspberry pi network projects, you quickly realize that running a single service on a dedicated board is a waste of silicon. The Raspberry Pi 5 has more than enough headroom to act as a centralized IoT and network gateway. In this build, we are combining two of the most popular raspberry pi network projects—a Pi-hole network-wide ad blocker and an Eclipse Mosquitto MQTT broker—into a single, unified Docker environment. We will also wire up physical GPIO status LEDs and write a Python health-monitor script to give you instant bench-level feedback.
Project Overview & Difficulty Rating
Time to Complete: 90 minutes
Target Board Variant: Raspberry Pi 5 (4GB RAM) running Raspberry Pi OS (64-bit, Bookworm). Note: A Raspberry Pi 4 Model B (4GB) is fully compatible as a fallback, but the Pi 5's PCIe and USB3 speeds make Docker container I/O significantly snappier.
This guide assumes you have already flashed your microSD card or NVMe drive with the 64-bit Bookworm release and have SSH access to your Pi. By containerizing these services, we prevent dependency conflicts and make the entire stack portable.
Hardware Bill of Materials (BOM)
Do not cheap out on the power supply or storage. Network projects that handle DNS and MQTT packet routing require stable voltage and fast random I/O. Here is the exact spec sheet for a reliable 2026 build:
| Component | Exact Variant / Model | Approx. Cost |
|---|---|---|
| Microcomputer | Raspberry Pi 5 (4GB RAM) | $60.00 |
| Power Supply | Official Raspberry Pi 27W USB-C PD (White/Black) | $12.00 |
| Storage | Samsung PRO Endurance 64GB microSD (or 128GB NVMe via PCIe HAT) | $14.00 |
| Cooling | Official Raspberry Pi Active Cooler | $5.00 |
| GPIO Indicators | 2x 5mm LEDs (Green/Red), 2x 330Ω resistors, jumper wires | $2.00 |
Deploying the Network Services via Docker
Running Pi-hole and Mosquitto natively via apt often leads to port conflicts and messy uninstalls. We will use Docker Compose. For deeper configuration options, refer to the official Pi-hole Docker documentation.
- Install Docker and Docker Compose:
curl -sSL https://get.docker.com | sh
Add your user to the docker group:sudo usermod -aG docker $USER(log out and back in to apply). - Create the Project Directory:
mkdir -p ~/iot-gateway && cd ~/iot-gateway - Create the Mosquitto Config File:
Create a directory for Mosquitto:mkdir -p mosquitto/config mosquitto/data mosquitto/log
Createmosquitto/config/mosquitto.confand add:
Note: For production, disable anonymous access and configurepersistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log listener 1883 allow_anonymous truepassword_file, but we use anonymous here for initial local testing. - Write the Docker Compose File:
Createdocker-compose.ymlin the~/iot-gatewaydirectory:version: '3.8' services: pihole: container_name: pihole image: pihole/pihole:latest ports: - '53:53/tcp' - '53:53/udp' - '8080:80/tcp' environment: TZ: 'America/New_York' WEBPASSWORD: 'your_secure_password_here' volumes: - './pihole/etc-pihole/:/etc/pihole/' - './pihole/etc-dnsmasq.d/:/etc/dnsmasq.d/' restart: unless-stopped mosquitto: container_name: mosquitto image: eclipse-mosquitto:2 ports: - '1883:1883' - '9001:9001' volumes: - './mosquitto/config/mosquitto.conf:/mosquitto/config/mosquitto.conf' - './mosquitto/data:/mosquitto/data' - './mosquitto/log:/mosquitto/log' restart: unless-stopped - Launch the Stack:
docker compose up -d
8080 instead of 80. This prevents conflicts if you later decide to add Home Assistant or Nginx to this same gateway.
GPIO Pin Mapping and Python Health Monitor
Headless network boxes are great until they silently fail. We will wire two LEDs to the Pi's GPIO header to give us physical, at-a-glance status of the MQTT broker and the Pi-hole DNS service.
Pin Mapping Table (BCM Numbering)
| Function | BCM GPIO Pin | Physical Pin | Component |
|---|---|---|---|
| MQTT Status LED | GPIO 17 | Pin 11 | Green LED + 330Ω Resistor |
| DNS/Pi-hole Status LED | GPIO 27 | Pin 13 | Red LED + 330Ω Resistor |
| Common Ground | GND | Pin 9 | Shared LED Cathodes |
Python Health Monitor Script
This script uses the gpiozero library (native to Bookworm) and the paho-mqtt v2.0 client. It pings the MQTT broker and checks the Pi-hole local DNS port, updating the LEDs accordingly.
First, install the MQTT library: pip install paho-mqtt
import time
import socket
import paho.mqtt.client as mqtt
from gpiozero import LED
from signal import pause
# --- PIN DEFINITIONS ---
PIN_MQTT_LED = 17
PIN_DNS_LED = 27
# Initialize GPIO
mqtt_led = LED(PIN_MQTT_LED)
dns_led = LED(PIN_DNS_LED)
# --- NETWORK TARGETS ---
MQTT_HOST = 'localhost'
MQTT_PORT = 1883
DNS_HOST = 'localhost'
DNS_PORT = 53
def check_dns_port():
"""Checks if Pi-hole is listening on port 53."""
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.settimeout(2)
result = s.connect_ex((DNS_HOST, DNS_PORT))
return result == 0
except Exception:
return False
def on_connect(client, userdata, flags, reason_code, properties):
"""Paho MQTT v2.0 connection callback."""
if reason_code == 0:
mqtt_led.on()
print('[OK] MQTT Broker connected.')
else:
mqtt_led.off()
print(f'[FAIL] MQTT connection failed with code: {reason_code}')
def on_disconnect(client, userdata, flags, reason_code, properties):
"""Handles unexpected disconnects."""
mqtt_led.off()
print(f'[WARN] MQTT disconnected. Reason: {reason_code}')
def main():
# Initialize MQTT Client (Using CallbackAPIVersion.VERSION2 for paho-mqtt >= 2.0)
client = mqtt.Client(callback_api_version=mqtt.CallbackAPIVersion.VERSION2)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
try:
client.connect(MQTT_HOST, MQTT_PORT, 60)
client.loop_start()
except ConnectionRefusedError as e:
print(f'Initial MQTT connection refused: {e}')
mqtt_led.off()
except Exception as e:
print(f'Unexpected MQTT error: {e}')
mqtt_led.off()
print('Starting network health monitor loop...')
try:
while True:
# Check DNS/Pi-hole
if check_dns_port():
dns_led.on()
else:
dns_led.off()
print('[WARN] Pi-hole DNS port 53 unreachable.')
time.sleep(5)
except KeyboardInterrupt:
print('\nShutting down monitor...')
finally:
client.loop_stop()
client.disconnect()
mqtt_led.off()
dns_led.off()
print('GPIO cleaned up. Exiting.')
if __name__ == '__main__':
main()
Debugging: Connection Refused and Network Drops
When combining raspberry pi network projects, port collisions and Docker bridge networking issues are your primary enemies. If your Python script or external IoT devices fail to connect to the gateway, you will likely encounter this exact error string in your terminal:
ConnectionRefusedError: [Errno 111] Connection refused
The First Three Things to Check When It Fails
- Verify Container Status: Run
docker ps -a. If the Mosquitto container is in a restart loop (statusRestarting), it means yourmosquitto.confhas a syntax error or the/mosquitto/datadirectory lacks write permissions. Fix permissions withsudo chown -R 1883:1883 mosquitto/. - Check Port Collisions: Run
sudo ss -tulpn | grep -E '53|1883|80'. If a native service (likesystemd-resolved) is hogging port 53, Pi-hole will crash on startup. Disable the native resolver viasudo systemctl disable systemd-resolvedand reboot. - Inspect Docker Bridge Firewall Rules: If the containers are running but external devices (or the host itself) get 'Connection Refused', your host firewall (
ufworiptables) might be blocking the Docker bridge. Allow the ports explicitly:sudo ufw allow 1883/tcpandsudo ufw allow 53.
Ranked Causes for 'Connection Refused' (Mosquitto Specific)
- Mosquitto v2 Default Behavior (Most Likely): Eclipse Mosquitto 2.0+ changed its default security model. If you do not explicitly define a
listener 1883andallow_anonymous true(or configure a password file) in your config, it will only listen on localhost and reject external connections. See the Eclipse Mosquitto documentation for migration details. - Incorrect Docker Port Mapping: Ensure your
docker-compose.ymlmaps'1883:1883'and not just'1883'(which assigns a random host port). - Stale Persistence Lock: If the Pi lost power, the
mosquitto.dbpersistence file might be locked or corrupted. Delete the file in themosquitto/datafolder and restart the container.
How to Extend or Simplify the Build
Not every deployment needs the full stack, and some need much more. Here is how to scale this project to your specific bench or home requirements.
How to Simplify the Build
- Drop the Python Monitor: If you don't want to wire physical LEDs, you can delete the Python script entirely and rely on Docker's built-in restart policies (
restart: unless-stopped) combined with a remote monitoring tool like Uptime Kuma. - Use a Pi Zero 2 W: If you only need Pi-hole (and drop the MQTT broker and Python script), a Pi Zero 2 W ($15) with 512MB RAM is sufficient for basic DNS ad-blocking for a small household.
How to Extend the Build
- Add Zigbee2MQTT: Plug a Sonoff Zigbee 3.0 USB Dongle Plus-P into the Pi 5's USB 2.0 port (to avoid 3.0 interference). Add the
koenkk/zigbee2mqttimage to yourdocker-compose.ymlto turn this gateway into a full smart-home hub. - Migrate to NVMe Storage: MicroSD cards will eventually wear out from Docker container logging and MQTT persistence writes. Use the Pi 5's PCIe 2.0 lane with a compatible NVMe HAT and a 256GB M.2 SSD for enterprise-grade reliability.
Frequently Asked Questions
What are the best raspberry pi network projects for beginners?
If this combined gateway feels too complex, start with a standalone Pi-hole installation. It requires zero coding, uses a simple web-based installer (curl -sSL https://install.pi-hole.net | bash), and immediately provides visible value by blocking ads on your phone and smart TV. Once you understand basic Linux networking and static IPs, move on to MQTT brokers or DNS-over-HTTPS (DoH) proxies.
Can I run multiple raspberry pi network projects on a single Pi 5?
Absolutely. The Raspberry Pi 5 (4GB or 8GB) is essentially a low-power desktop. Using Docker Compose, you can easily run Pi-hole, Mosquitto, Home Assistant, Node-RED, and AdGuard Home simultaneously. The bottleneck is rarely CPU; it is usually RAM (if running Java-based apps) or storage I/O (if using a cheap microSD card). Always use high-endurance storage when stacking multiple database-heavy network projects.
Why does my Pi drop WiFi when running heavy network projects?
The onboard WiFi/Bluetooth chip on Raspberry Pi boards shares the SDIO bus and internal antennas. Heavy network throughput (like downloading large OTA updates for smart devices via MQTT while simultaneously filtering DNS) can cause the WiFi chip to overheat or hit driver buffer limits. For any serious network gateway project, always use a hardwired Ethernet connection. If you must use WiFi, disable power management via sudo iwconfig wlan0 power off to prevent the radio from sleeping during micro-bursts of traffic.
Do I need a static IP for raspberry pi network projects?
Yes. If your Pi is acting as a DNS server (Pi-hole) or an MQTT broker, its IP address cannot change. If the router assigns it a new DHCP lease, all your IoT devices and browser DNS settings will point to a dead address. Set a static IP reservation in your router's DHCP settings tied to the Pi's MAC address, or configure a static IP directly in the Pi's /etc/NetworkManager/system-connections/ directory.






