TL;DR: A Raspberry Pi can run your entire privacy infrastructure: Pi-hole blocks ads and trackers for every device on your network, WireGuard provides a VPN to access your home network securely from anywhere, and Nextcloud gives you private cloud storage. Total cost: under $100 for hardware that runs 24/7 on about 5 watts. This guide walks through setting up all three services using Docker.

What We're Building

A single Raspberry Pi running three privacy-enhancing services:

Service What It Does
Pi-hole Network-wide ad and tracker blocking via DNS
WireGuard VPN server for secure remote access
Nextcloud Private cloud storage (Google Drive alternative)

Why This Matters

  • Pi-hole: Blocks ads, trackers, and malware domains before they load, on every device, including smart TVs and IoT devices that can't run ad blockers
  • WireGuard: Access your home network from anywhere without exposing services to the internet. Your data stays out of commercial VPN providers' hands
  • Nextcloud: Your files, on your hardware. No scanning, no AI training, no terms of service changes

Hardware Requirements

Recommended Setup

Component Recommendation Approx. Cost
Raspberry Pi Pi 5 (4GB+) or Pi 4 (4GB+) $60-80
MicroSD Card 32GB+ high endurance $10-15
Power Supply Official USB-C (5V 5A for Pi 5) $12
Case With cooling (passive or active) $10-20
Ethernet Cable Wired connection recommended $5

Optional but recommended: USB SSD for Nextcloud storage (much faster and more reliable than SD card).

Minimum Viable Setup

A Raspberry Pi 4 with 2GB RAM can run Pi-hole and WireGuard. Add Nextcloud only with 4GB+ RAM.

Initial Raspberry Pi Setup

Step 1: Flash Raspberry Pi OS

  1. Download Raspberry Pi Imager
  2. Choose "Raspberry Pi OS Lite (64-bit)", no desktop needed
  3. Click the gear icon for advanced options:
    • Enable SSH
    • Set username and password
    • Configure WiFi (if not using Ethernet)
    • Set locale/timezone
  4. Flash to SD card

Step 2: First Boot and Update


# Find your Pi's IP address from your router, then SSH in
ssh pi@YOUR_PI_IP

# Update everything
sudo apt update && sudo apt upgrade -y

# Set a static IP (recommended)
sudo nmtui
# Select "Edit a connection" > Your interface > IPv4 > Manual
# Set your desired static IP, gateway, and DNS
            

Step 3: Install Docker


# Install Docker
curl -sSL https://get.docker.com | sh

# Add your user to docker group
sudo usermod -aG docker $USER

# Log out and back in
exit
ssh pi@YOUR_PI_IP

# Verify Docker works
docker --version
            

Service 1: Pi-hole (Ad Blocking)

Pi-hole acts as a DNS sinkhole, blocking requests to known advertising, tracking, and malware domains before they reach your devices.

Create Directory Structure


mkdir -p ~/docker/pihole
cd ~/docker/pihole
            

Create Docker Compose File

Create docker-compose.yml:


version: "3"

services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "80:80/tcp"
    environment:
      TZ: 'America/New_York'
      WEBPASSWORD: 'YOUR_SECURE_PASSWORD'
    volumes:
      - './etc-pihole:/etc/pihole'
      - './etc-dnsmasq.d:/etc/dnsmasq.d'
    restart: unless-stopped
            

Start Pi-hole


docker compose up -d

# Check it's running
docker ps
            

Configure Your Network

Option A: Router-level (recommended)

  1. Log into your router's admin panel
  2. Find DHCP settings
  3. Set DNS server to your Pi's IP address
  4. All devices will automatically use Pi-hole

Option B: Per-device

Manually set DNS on each device to your Pi's IP address.

Access Pi-hole Dashboard

Navigate to http://YOUR_PI_IP/admin

Add More Blocklists

In the Pi-hole admin panel, go to Adlists and add:

  • https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts
  • https://raw.githubusercontent.com/PolishFiltersTeam/KADhosts/master/KADhosts.txt
  • https://someonewhocares.org/hosts/zero/hosts

Service 2: WireGuard (VPN)

WireGuard lets you securely access your home network from anywhere. When connected, you'll also use Pi-hole for DNS, ad blocking on the go.

Create Directory and Compose File


mkdir -p ~/docker/wireguard
cd ~/docker/wireguard
            

Create docker-compose.yml:


version: "3"

services:
  wireguard:
    image: linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/New_York
      - SERVERURL=YOUR_DDNS_OR_PUBLIC_IP
      - SERVERPORT=51820
      - PEERS=phone,laptop,tablet
      - PEERDNS=YOUR_PI_LOCAL_IP
      - INTERNAL_SUBNET=10.13.13.0
    volumes:
      - ./config:/config
      - /lib/modules:/lib/modules
    ports:
      - 51820:51820/udp
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1
    restart: unless-stopped
            

Replace:

  • YOUR_DDNS_OR_PUBLIC_IP with your public IP or dynamic DNS hostname
  • YOUR_PI_LOCAL_IP with your Pi's local IP (e.g., 192.168.1.50)

Start WireGuard


docker compose up -d

# View generated QR codes for mobile setup
docker logs wireguard
            

Router Configuration

Forward UDP port 51820 from your router to your Pi's IP address.

Dynamic DNS (If Needed)

If your ISP changes your IP, use a free DDNS service:

Connect Clients

Download WireGuard app on your phone/laptop and scan the QR code from the logs, or copy the config files from ~/docker/wireguard/config/peer_*/.

Service 3: Nextcloud (Private Cloud)

Nextcloud provides file storage, calendar, contacts, and more, all under your control.

Create Directory Structure


mkdir -p ~/docker/nextcloud
cd ~/docker/nextcloud
            

Create Docker Compose File


version: "3"

services:
  nextcloud:
    image: nextcloud:latest
    container_name: nextcloud
    ports:
      - "8080:80"
    volumes:
      - ./nextcloud:/var/www/html
      - ./data:/var/www/html/data
    environment:
      - MYSQL_HOST=db
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=YOUR_DB_PASSWORD
    depends_on:
      - db
    restart: unless-stopped

  db:
    image: mariadb:10
    container_name: nextcloud-db
    environment:
      - MYSQL_ROOT_PASSWORD=YOUR_ROOT_PASSWORD
      - MYSQL_DATABASE=nextcloud
      - MYSQL_USER=nextcloud
      - MYSQL_PASSWORD=YOUR_DB_PASSWORD
    volumes:
      - ./db:/var/lib/mysql
    restart: unless-stopped
            

Start Nextcloud


docker compose up -d
            

Initial Setup

  1. Navigate to http://YOUR_PI_IP:8080
  2. Create admin account
  3. Database is already configured via environment variables

External Storage (Recommended)

For better performance and longevity, mount a USB SSD:


# Find your drive
lsblk

# Format if needed (WARNING: erases data)
sudo mkfs.ext4 /dev/sda1

# Create mount point
sudo mkdir /mnt/nextcloud-data

# Mount
sudo mount /dev/sda1 /mnt/nextcloud-data

# Add to fstab for auto-mount
echo '/dev/sda1 /mnt/nextcloud-data ext4 defaults 0 2' | sudo tee -a /etc/fstab

# Update docker-compose.yml data volume:
# - /mnt/nextcloud-data:/var/www/html/data
            

Access via VPN

With WireGuard running, you can access Nextcloud from anywhere through your VPN, no need to expose it to the public internet.

Security Hardening

Firewall


sudo apt install ufw -y

# Allow SSH
sudo ufw allow 22/tcp

# Allow DNS (Pi-hole)
sudo ufw allow 53/tcp
sudo ufw allow 53/udp

# Allow Pi-hole web interface (local only)
sudo ufw allow from 192.168.1.0/24 to any port 80

# Allow WireGuard
sudo ufw allow 51820/udp

# Allow Nextcloud (local only)
sudo ufw allow from 192.168.1.0/24 to any port 8080

# Enable firewall
sudo ufw enable
            

Fail2ban

See our dedicated Fail2ban Setup Guide for protecting SSH and services from brute force attacks.

Automatic Updates


sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure unattended-upgrades
            

Docker Container Updates


# Update all containers
cd ~/docker/pihole && docker compose pull && docker compose up -d
cd ~/docker/wireguard && docker compose pull && docker compose up -d
cd ~/docker/nextcloud && docker compose pull && docker compose up -d
            

Optional: Portainer (Web Management)

Portainer provides a web interface for managing Docker containers.


docker volume create portainer_data

docker run -d -p 9000:9000 --name=portainer \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest
            

Access at http://YOUR_PI_IP:9000

Troubleshooting

Pi-hole Not Blocking Ads

  • Verify your device is using the Pi as DNS: nslookup google.com
  • Check Pi-hole is running: docker ps
  • Clear browser DNS cache and restart

WireGuard Won't Connect

  • Verify port 51820/UDP is forwarded on your router
  • Check your public IP or DDNS is correct in config
  • View logs: docker logs wireguard

Nextcloud Slow

  • Use USB SSD instead of SD card
  • Enable Redis caching (requires additional container)
  • Check Pi temperature: vcgencmd measure_temp

General Docker Issues


# View logs
docker logs container_name

# Restart container
docker restart container_name

# Check resource usage
docker stats
            

The Bottom Line

For under $100 in hardware and a few hours of setup, you have:

  • Network-wide ad and tracker blocking for every device
  • A personal VPN that doesn't log your data
  • Private cloud storage under your complete control

The Pi runs 24/7 on about 5 watts, less than a night light. Maintenance is minimal: occasional updates and backups. The privacy benefits are permanent: your data stays in your home, not on corporate servers.

References

  1. Pi-hole Official Documentation
  2. WireGuard Official Site
  3. Nextcloud Documentation
  4. XDA, WireGuard VPN on Raspberry Pi 5
  5. GitHub, Pi-hole + Unbound + WireGuard
  6. SunFounder, Raspberry Pi VPN Server Guide 2025