Post

How to Set Up Raspberry Pi with Docker, Portainer & iptables

How to Set Up Raspberry Pi with Docker, Portainer & iptables

Raspberry Pi has come a long way from being just a tinkering board. The latest models pack up to 16GB of RAM, making them powerful enough to act as a lightweight Docker server capable of running multiple containers. Setting up a Raspberry Pi as part of your homelab is an excellent way to learn about servers, containers, and network security in a low-power, cost-effective environment.

With Docker and Portainer installed, you can quickly deploy and manage applications through both the command line and a clean web interface. By adding iptables firewall rules, you’ll ensure your system is secure, protecting your services while still giving you full control over what to expose.

This guide will walk you through:

  1. Setting up Raspberry Pi OS
  2. Configuring cooling, networking, and security
  3. Installing Docker and Portainer
  4. Setting up firewall rules with iptables

Part 1 – Raspberry Pi OS Setup

Step 1 – Download Raspberry Pi Imager

Download the official Raspberry Pi Imager: πŸ‘‰ Raspberry Pi Software


Step 2 – Flash the OS to the SD Card

Use the Raspberry Pi Imager to flash the OS. During setup, configure:

  • βœ… Enable SSH
  • βœ… Set Wi-Fi SSID & password
  • βœ… Set hostname
  • βœ… Create username & password

Step 3 – Boot the Raspberry Pi

Insert the SD card into the Raspberry Pi and power it on.


Step 4 – Update the System

Run the following to update all packages:

1
sudo apt update && sudo apt upgrade -y

Step 5 – Configure Fan, Wi-Fi, and Bluetooth

Edit the configuration file:

1
sudo nano /boot/firmware/config.txt

Add these lines at the bottom:

1
2
3
4
5
6
7
8
9
10
11
[all]
# Fan control settings
dtparam=fan_temp0=35000
dtparam=fan_temp0_hyst=5000
dtparam=fan_temp0_speed=175

# Disable Wi-Fi
dtoverlay=disable-wifi

# Disable Bluetooth
dtoverlay=disable-bt

Save, exit, and reboot.


Step 6 – Assign a Static IP

1. List connections

1
nmcli connection show

Find the connection linked to eth0 (e.g., Wired connection 1).

2. Set static IP

Replace <conn-name> with your connection name:

1
2
3
4
sudo nmcli connection modify "<conn-name>" ipv4.addresses 192.168.1.50/24
sudo nmcli connection modify "<conn-name>" ipv4.gateway 192.168.1.1
sudo nmcli connection modify "<conn-name>" ipv4.dns 8.8.8.8
sudo nmcli connection modify "<conn-name>" ipv4.method manual

3. Apply changes

1
2
sudo nmcli connection down "<conn-name>"
sudo nmcli connection up "<conn-name>"

⚠️ Warning: If you’re on SSH, you’ll be disconnected after down.

4. Verify IP

1
ip addr show eth0

5. Test internet

1
ping google.com

Part 2 – Install Docker

Follow the official Docker guide: πŸ‘‰ Install Docker Engine

Then complete the post-install steps: πŸ‘‰ Linux Post-Installation Guide


Part 3 – Install Portainer

What is Portainer?

Portainer is a simple web UI for managing Docker containers. It makes it much easier for beginners to deploy and monitor containers without using only the command line.

πŸ‘‰ Portainer Official Guide

πŸ’‘ Note: Portainer Business Edition (BE) includes a free plan for up to 3 nodes.


Part 4 – Configure iptables Firewall

Use the following script to secure your system with iptables:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# Check if the script is being run as root
if [ "$EUID" -ne 0 ]; then
  echo "Please run as root or with sudo"
  exit 1
fi

# Function to prompt for persistent firewall installation
install_persistent() {
  read -p "iptables-persistent/iptables-services not installed. Install now? (yes/no): " choice
  case "$choice" in
    y|Y|yes|Yes )
      if [ -x "$(command -v apt-get)" ]; then
        apt-get update && apt-get install -y iptables-persistent || { echo "Installation failed."; exit 1; }
      elif [ -x "$(command -v yum)" ]; then
        yum install -y iptables-services || { echo "Installation failed."; exit 1; }
      else
        echo "Unknown package manager. Install manually."
        exit 1
      fi
      ;;
    * )
      echo "Skipping installation. Rules won’t persist after reboot."
      ;;
  esac
}

# Detect system type
if [ -x "$(command -v dpkg-query)" ]; then
  SYSTEM="debian"
  dpkg-query -W iptables-persistent &>/dev/null || install_persistent
elif [ -x "$(command -v rpm)" ]; then
  SYSTEM="redhat"
  rpm -q iptables-services &>/dev/null || install_persistent
else
  echo "Unsupported system."
  exit 1
fi

# Reset existing rules
iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -t raw -F
iptables -t raw -X

# Default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT

# Allow loopback
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

# Allow established connections
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

# SSH protection
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# Optional: Web traffic
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT

# Optional: Portainer ports
iptables -A INPUT -p tcp --dport 81 -j ACCEPT
iptables -A INPUT -p tcp --dport 9443 -j ACCEPT

# Logging
iptables -A INPUT -m limit --limit 5/min --limit-burst 10 -j LOG --log-prefix "iptables denied: " --log-level 7

# Save configuration
if [ "$SYSTEM" == "debian" ]; then
  iptables-save > /etc/iptables/rules.v4
elif [ "$SYSTEM" == "redhat" ]; then
  service iptables save
fi

echo "iptables rules configured and saved."
echo "Check with: sudo iptables -L -v"

βœ… At this point your Raspberry Pi should be:

  • Running the latest OS
  • Secured with a firewall
  • Ready with Docker & Portainer
This post is licensed under CC BY 4.0 by the author.