home server

building a self-hosted NAS with cloud services on a Raspberry Pi 5

From Single-Board Computer to Personal Cloud

Modern cloud storage is convenient, but comes at a cost—privacy, ongoing fees, and vendor lock-in. This project turns a Raspberry Pi 5 into a full-featured Network Attached Storage system that rivals commercial NAS solutions, running an entire self-hosted cloud stack: file sync, photo backup, smart home automation, and more—all on local hardware, accessible from anywhere via VPN.

The result is a low-power, always-on personal server with 2.7TB of NAS-grade storage, running Nextcloud, Immich, and Home Assistant inside Docker containers, protected by SnapRAID parity and accessible remotely via Tailscale—for a fraction of what cloud subscriptions would cost.


Project Overview

This project builds a home NAS from a Raspberry Pi 5 paired with the Radxa Penta SATA HAT, enabling up to five 2.5” SATA drives directly connected to the board. OpenMediaVault provides the NAS OS and management interface, while Docker containers power the application layer on top.

Key Features

  • ✅ 2.7TB Storage Pool - mergerfs spanning 3× WD Red NAS SSDs
  • ✅ SnapRAID Parity - Data protection against single-drive failure
  • ✅ Nextcloud - Self-hosted Google Drive replacement
  • ✅ Immich - Google Photos replacement with mobile auto-backup
  • ✅ Home Assistant - Smart home hub with sensor monitoring
  • ✅ Tailscale VPN - Secure remote access, no port forwarding
  • ✅ SMB Shares - Auto-mounted in macOS Finder on login
  • ✅ Daily Backups - Automated rsync vault backup via OMV scheduler

Hardware

The hardware stack centers on the Raspberry Pi 5, chosen for its significant performance jump over the Pi 4 and its support for PCIe—which the Radxa Penta SATA HAT exploits to attach five SATA ports via a PCIe-to-SATA bridge.

Component Specification
SBC Raspberry Pi 5 (16GB RAM)
SATA Expansion Radxa Penta SATA HAT + Top Board
Primary Storage 3× WD Red 1TB NAS SSD (mergerfs pool)
Secondary Storage Kingston A400 480GB (Docker + backups)
Cooling Official Raspberry Pi 5 Active Cooler + HAT fan header
Power Supply 12V 6A barrel jack (powers HAT + Pi via GPIO)
Boot Media MicroSD card (119GB, OS only)

Raspberry Pi 5 with the Radxa Penta SATA HAT stacked on top via GPIO extension—four SSDs installed, no case
Finished NAS inside a custom 3D printed case with ventilation slits, top-mounted fan, and SSD activity LEDs visible through the side

Hardware Challenge — GPIO Interference: The Pi 5 Active Cooler’s power connector sits directly where the Penta SATA HAT Top Board connects to the GPIO header. The solution is a GPIO extension cable that raises the Top Board just enough to clear the cooler, without removing it. Stack order: Pi 5 → GPIO extension → Top Board → SATA HAT.


Software Stack

NAS OS

OpenMediaVault 8 (OMV)

Debian-based NAS platform providing web UI for storage management, SMB, SMART monitoring, and scheduled tasks.

Storage Layer

mergerfs + SnapRAID

mergerfs presents multiple drives as one unified volume; SnapRAID adds parity protection using the Kingston drive as a dedicated parity disk.

Container Runtime

Docker + Portainer

Docker manages all application containers; Portainer provides a web UI for container lifecycle management and compose deployments.

Remote Access

Tailscale

WireGuard-based mesh VPN. No port forwarding, no dynamic DNS—every device joins the same private network regardless of location.

Application Containers:

  • Nextcloud — Self-hosted file sync and cloud storage (port 8080)
  • Immich — AI-powered photo library with mobile app auto-backup (port 2283)
  • Home Assistant — Smart home platform, also used for GPIO-based fan control (port 8123)

Access Methods

🔐 SSH

Remote terminal access

ssh user@pi5-nas.local

Full terminal for administration, log inspection, scripting, and ad-hoc commands.

🌐 Web UIs

Browser-based management

http://pi5-nas.local

OMV dashboard, Portainer, Nextcloud, Immich, and Home Assistant each expose their own web interface.

📁 SMB Shares

macOS Finder integration

smb://pi5-nas/Documents

Documents, Photos, and Media shares auto-mount on Mac login via LaunchAgent. Credentials stored in Keychain.


Storage Architecture

The drives are assigned distinct roles to balance performance, longevity, and data safety:

WD Red Pool (2.7TB) — mergerfs across 3× WD Red 1TB
├── Documents/       → Nextcloud external storage
├── Photos/          → Immich photo library
├── Media/           → Jellyfin content (Movies, TV, Music)
├── Backups/         → System and config backups
└── Docker/          → Compose files and app configs

Kingston A400 (480GB) — dedicated secondary drive
├── docker/          → Docker engine root (images + volumes)
└── Backups/Documents_Vault/   → Daily rsync from WD pool (2AM)

SnapRAID parity is stored on the Kingston drive. Since parity is written only during nightly syncs, the write-heavy main data stays on the more durable WD Red drives, while the Kingston’s lower endurance rating is well-suited to the read-heavy parity workload.


Implementation Highlights

Fan Control via Home Assistant

The Radxa Penta Top Board exposes a 4-pin PWM fan header. Rather than a standalone service, fan control runs as a Home Assistant automation: a temperature sensor monitors CPU temperature, and automations trigger GPIO commands to spin the fan up above 55°C and back down below 50°C. This integrates hardware monitoring with the smart home dashboard.

Network Recovery from Bad netplan Config

A sudo omv-salt deploy run compose call regenerated the netplan network config using the interface name stored in OMV’s config.xml—which had been set to eth0. On the Pi 5, the actual Ethernet interface is end0. The Pi became completely unreachable: no IP, no mDNS, no Tailscale.

Recovery required mounting the SD card on a separate Linux machine, reading the systemd journal from the last boot to confirm the true interface name, then patching both the netplan file and config.xml—because patching only the netplan file would be overwritten on the next salt deploy.

# Fix the regenerated netplan
sudo sed -i 's/eth0/end0/g' /mnt/etc/netplan/20-openmediavault-eth0.yaml

# Fix OMV's source-of-truth so it won't regenerate the wrong config again
sudo sed -i 's/<devicename>eth0<\/devicename>/<devicename>end0<\/devicename>/g' \
  /mnt/etc/openmediavault/config.xml

Automated Vault Backup

Critical files are placed in a Documents/Vault/ folder on the WD Red pool. A daily rsync job scheduled in OMV copies the Vault to the Kingston drive at 2 AM. This gives a two-drive separation for the most important data without complex RAID.


Services at a Glance

2.7TB

Usable Storage
(mergerfs pool)

5

SATA Ports
(Penta HAT)

~110MB/s

Network Speed
(GbE practical)

~15W

Power Draw
(typical)


Use Cases

  • 📂 Personal Cloud - Nextcloud replaces Google Drive / iCloud for file sync across devices
  • 📸 Photo Backup - Immich automatically backs up the camera roll, with facial recognition and timeline view
  • 🏠 Smart Home Hub - Home Assistant controls devices, monitors sensors, and manages fan speed
  • 🎬 Media Server - Centralized storage ready for Jellyfin and automated media management
  • 🔐 Zero-Trust Remote Access - Tailscale VPN provides full access from any network without exposing ports
  • 🎓 Homelab Platform - Hands-on experience with Linux, Docker, networking, and storage concepts

Key Challenges

1. Physical Hardware Compatibility
The Active Cooler power connector physically blocks the Penta HAT Top Board. No modification to either component is needed—GPIO extension pins solve it cleanly.

2. Drive Endurance Mismatch
The Kingston A400 is not rated for 24/7 NAS workloads. Assigning it to the parity role (read-heavy, writes only during nightly sync) dramatically extends its lifespan—estimated at 10–15 years in this configuration vs. 1–2 years as a primary data drive.

3. Interface Naming on Pi 5
The Pi 5’s Ethernet adapter is end0, not the conventional eth0. OMV-managed network configs must use the correct name, or any subsequent salt deploy will silently break networking.

4. SD Card Longevity
The OS SD card is the weakest link for long-term reliability. Docker’s data root is redirected to the Kingston SSD, and the OMV flashmemory plugin reduces unnecessary writes to the card.


Security Practices

  • 🔐 Default passwords changed on all services immediately after installation
  • 🔑 SSH key authentication for remote login
  • 🌐 No open ports to the internet—all remote access via Tailscale
  • 🔄 Automatic apt updates + monthly Docker image updates
  • 📧 SMART monitoring with email alerts on drive health changes
  • 🛡️ 2FA enabled on Nextcloud and Immich

Future Plans

  • 📺 Jellyfin - Media streaming server with library auto-management
  • 🌍 Pi-hole migration - Move DNS ad-blocking from a Pi Zero to the NAS
  • 📊 Grafana dashboard - InfluxDB + Telegraf for long-term drive health graphs
  • 🔄 DHCP reservation - Lock the Pi’s IP in the router for zero-configuration stability
  • 💿 4th WD Red drive - Expand the storage pool and introduce a dedicated SnapRAID parity drive

Lessons Learned

What Worked Well

  • mergerfs + SnapRAID is an excellent combination for home NAS—flexible pool sizing with meaningful data protection, without the complexity of traditional RAID
  • Docker + Portainer makes running multiple services approachable; updating or rolling back a service is a two-click operation
  • Tailscale eliminated every remote access headache—no port forwarding, no DDNS, no firewall rules; it just works
  • The Pi 5’s PCIe bus proved genuinely capable: sustained ~110 MB/s over GbE without saturating the SATA controller
  • Home Assistant for fan control was a creative reuse—hardware monitoring integrated directly into the smart home dashboard with no separate daemon

Areas for Improvement

  • SD card as boot media is the weakest reliability link; migrating the OS to the Kingston SSD (via raspi-config) would eliminate this single point of failure
  • Kingston A400 in the parity role reduces write wear significantly, but it is not NAS-rated—long-term endurance in this role is still to be proven
  • No automated container health alerts yet; a watchdog or uptime monitoring tool (e.g., Uptime Kuma) is needed before this qualifies as a truly hands-off setup
  • Static IP is still assigned via DHCP lease rather than a true reservation in the router—a stale lease could break mDNS and Tailscale exit routing

Complete Documentation

This project is fully documented with step-by-step guides for every phase of the build—from hardware assembly through each service deployment:

Repository Contents

  • docs/ — Nine sequential guides covering hardware through every service (OMV, storage, Docker, Nextcloud, Immich, Home Assistant, Tailscale, backups)
  • configs/ — Configuration file examples with comments
  • scripts/ — Automation and utility scripts
  • images/ — Build photographs

Community Contribution

This project is open source and welcomes contributions:

  • Hardware compatibility reports — Tested on a different Pi revision or SATA HAT? Share your results
  • Documentation improvements — Found a clearer way to explain a step?
  • Troubleshooting solutions — Hit an issue and solved it? Add it to the docs
  • Service additions — Running Jellyfin, Pi-hole, or something else on top? Pull requests welcome

Visit the Contributing Guidelines to get involved.


Project Status: ✅ Operational
Last Updated: June 2026


References & Resources

These resources were instrumental in planning and building this project: