Raspberry Pi Home Server: Host Your Own Cloud, Media and More

!DOCTYPE html> Raspberry Pi Home Server: NAS, VPN, Pi-hole, and Docker in One Box

Raspberry Pi Home Server: NAS, VPN, Pi-hole, and Docker in One Box

Turn a Raspberry Pi 4 into a powerful self-hosted home server running a NAS, network-wide ad blocker, VPN, and all your favourite services in Docker -- for under $80.

Hardware Requirements

ComponentRecommendedNotes
BoardRaspberry Pi 4 (4GB or 8GB)Pi 5 also works and is faster
Boot drive32GB+ quality microSD or USB SSDSSD strongly recommended -- SD cards fail under write load
Storage1-4 TB USB 3.0 HDD or SSDUSB-powered HDD needs a powered hub
Power supplyOfficial 5V/3A USB-C PSUCheap PSUs cause instability
CaseArgon ONE or similar with coolingActive cooling critical for sustained loads
NetworkWired Ethernet (not WiFi)WiFi adds latency and instability to NAS

OS Setup and Hardening

# 1. Flash Raspberry Pi OS Lite (64-bit) using Raspberry Pi Imager
#    Enable SSH and set hostname/username in Imager's advanced settings

# 2. First login -- update everything
sudo apt update && sudo apt full-upgrade -y

# 3. Set static IP (edit /etc/dhcpcd.conf)
sudo nano /etc/dhcpcd.conf
# Add to /etc/dhcpcd.conf:
interface eth0
static ip_address=192.168.1.10/24
static routers=192.168.1.1
static domain_name_servers=127.0.0.1 1.1.1.1  # Pi-hole first
# 4. Secure SSH -- disable password login, use keys only
ssh-keygen -t ed25519 -C "pi-homeserver"
ssh-copy-id pi@192.168.1.10

sudo nano /etc/ssh/sshd_config
# Set: PasswordAuthentication no
# Set: PermitRootLogin no
# Set: PubkeyAuthentication yes
sudo systemctl restart sshd

# 5. Enable UFW firewall
sudo apt install ufw -y
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp    # HTTP
sudo ufw allow 443/tcp   # HTTPS
sudo ufw allow 53        # DNS (Pi-hole)
sudo ufw allow 51820/udp # WireGuard VPN
sudo ufw enable

# 6. Set timezone
sudo timedatectl set-timezone Asia/Kolkata  # change to your TZ

# 7. Enable automatic security updates
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure --priority=low unattended-upgrades

External Storage and Automount

# Identify your USB drive
lsblk

# Format as ext4 (do this ONCE -- erases the drive)
sudo mkfs.ext4 -L NASHDD /dev/sda1

# Create mount point
sudo mkdir -p /mnt/nas

# Get UUID for reliable mounting
sudo blkid /dev/sda1
# -> UUID="a1b2c3d4-..."

# Add to /etc/fstab for automount on boot
echo 'UUID=a1b2c3d4-xxxx  /mnt/nas  ext4  defaults,nofail,noatime  0  2' \
  | sudo tee -a /etc/fstab

# Mount now
sudo mount -a
df -h /mnt/nas

# Set permissions
sudo chown -R $USER:$USER /mnt/nas
sudo chmod 775 /mnt/nas

Docker and Portainer

# Install Docker (official script)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker

# Verify
docker run hello-world

# Install Portainer (web UI for Docker management)
docker volume create portainer_data
docker run -d \
  --name portainer \
  --restart=always \
  -p 9443:9443 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest

# Access at https://192.168.1.10:9443

Docker Compose Base Stack

# /opt/homeserver/docker-compose.yml
version: "3.9"

networks:
  homelab:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/24

volumes:
  pihole_config:
  pihole_dnsmasq:
  samba_config:
  grafana_data:
  prometheus_data:

Pi-hole: Network-Wide Ad Blocking

# Add to docker-compose.yml
services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    restart: unless-stopped
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80"         # admin UI
    environment:
      TZ: Asia/Kolkata
      WEBPASSWORD: "your-strong-password"
      PIHOLE_DNS_: "1.1.1.1;8.8.8.8"
      DNSSEC: "true"
    volumes:
      - pihole_config:/etc/pihole
      - pihole_dnsmasq:/etc/dnsmasq.d
    networks:
      homelab:
        ipv4_address: 172.20.0.2
# After starting -- point your router's DNS to 192.168.1.10
# Or set per-device DNS to 192.168.1.10

# Recommended blocklists to add in Pi-hole admin:
# https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
# https://adaway.org/hosts.txt
# https://v.firebog.net/hosts/AdguardDNS.txt

docker compose up -d pihole
# Access admin: http://192.168.1.10:8080/admin

Samba NAS

# Add to docker-compose.yml
  samba:
    image: dperson/samba:latest
    container_name: samba
    restart: unless-stopped
    ports:
      - "139:139"
      - "445:445"
    volumes:
      - /mnt/nas:/share
    command: >
      -u "pi;password123"
      -s "NAS;/share;yes;no;no;pi"
      -g "log level = 1"
    networks:
      - homelab
# Windows: map network drive -> \\192.168.1.10\NAS
# macOS:   Finder -> Go -> Connect to Server -> smb://192.168.1.10/NAS
# Linux:   mount -t cifs //192.168.1.10/NAS /mnt/pi-nas -o user=pi

WireGuard VPN -- Access Home from Anywhere

# Use wg-easy -- the simplest WireGuard setup
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:latest
    container_name: wg-easy
    restart: unless-stopped
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"   # web UI
    environment:
      WG_HOST: "your.public.ip.or.ddns.hostname"
      PASSWORD: "your-admin-password"
      WG_DEFAULT_DNS: "172.20.0.2"   # route DNS through Pi-hole!
      WG_ALLOWED_IPS: "0.0.0.0/0"   # route ALL traffic through VPN
    volumes:
      - /opt/homeserver/wireguard:/etc/wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    networks:
      - homelab
# Access admin UI: http://192.168.1.10:51821
# Create a client -> download QR code -> scan with WireGuard mobile app
# Now your phone has full access to your home network from anywhere

Monitoring with Grafana + Prometheus

# Add to docker-compose.yml
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    networks: [homelab]

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped
    pid: host
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command: >
      --path.procfs=/host/proc
      --path.rootfs=/rootfs
      --path.sysfs=/host/sys
      --collector.filesystem.mount-points-exclude='^/(sys|proc|dev|host|etc)($$|/)'
    networks: [homelab]

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      GF_SECURITY_ADMIN_PASSWORD: "your-password"
    volumes:
      - grafana_data:/var/lib/grafana
    networks: [homelab]
# prometheus.yml
global:
  scrape_interval: 15s
scrape_configs:
  - job_name: "node"
    static_configs:
      - targets: ["node-exporter:9100"]
# Start everything
cd /opt/homeserver
docker compose up -d

# Import Grafana dashboard for Pi monitoring:
# Grafana -> Import -> Dashboard ID: 1860 (Node Exporter Full)
# Access: http://192.168.1.10:3000

Keeping Everything Updated

# Install Watchtower to auto-update Docker images
docker run -d \
  --name watchtower \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower \
  --schedule "0 0 4 * * *"  # update at 4 AM daily
[YES] Boot from USB SSD: Use Raspberry Pi Imager to flash the OS to a USB SSD instead of a microSD. Then in raspi-config -> Advanced -> Boot Order, set USB boot priority. SSD gives 5-10x better I/O performance and dramatically better longevity for a 24/7 server.

Key Takeaways: Use a quality SSD, not microSD, for 24/7 server reliability. Hardwire Ethernet -- never run a NAS on WiFi. Deploy everything in Docker Compose for easy management and updates. Route VPN clients through Pi-hole DNS for ad-blocking on mobile too.

Raspberry PiHome ServerNASPi-holeWireGuardDockerSelf-Hosted

Post a Comment

0 Comments