A minimal, upgrade-proof home server for a Raspberry Pi: a network drive (NAS) over SMB and a remote coding host over SSH. Built to be lean enough for a Pi 3 and portable enough that moving to a Pi 4/5 later is a drive swap, not a rebuild.
Your whole server is just three things, none tied to this particular Pi:
- The 2 TB USB drive — all your files, mounted by UUID so it lands at the same path on any board or USB port.
- Your code in git — the Pi is a disposable compute host you SSH into. Clone your repos onto it; it holds nothing irreplaceable.
- This repo — captures the setup itself (Samba config + scripts).
To move to a new Pi: flash a fresh OS → git clone this repo → run the scripts →
plug the drive in. Ten minutes, nothing to reverse-engineer.
Samba and SSH are installed natively (they're standard, featherweight OS
services). Docker is installed too but sits idle — it's the substrate for
future add-ons (Home Assistant, Pi-hole, media, …), each of which drops into
docker/docker-compose.yml with its data on the drive.
- Raspberry Pi running Raspberry Pi OS Lite (64-bit), on your network.
- The 2 TB USB drive plugged in (its data will be erased in step 1).
- SSH into the Pi, then get this repo onto it:
git clone <your-fork-url> pi-home-server cd pi-home-server chmod +x setup.sh scripts/*.sh # in case exec bits didn't survive
- Open
config.envand adjust if you like (defaults are sensible).
Do these once, in order. Read scripts/format-drive.sh before running it — it
erases a whole disk.
# 1. Find your drive's device name. The USB drive is usually /dev/sda.
lsblk -o NAME,SIZE,TYPE,MODEL,MOUNTPOINT
# 2. Format it (DESTRUCTIVE — erases everything on that disk).
sudo ./scripts/format-drive.sh /dev/sda
# 3. Mount it permanently by UUID (the previous step prints the partition, e.g. /dev/sda1).
sudo ./scripts/mount-drive.sh /dev/sda1
# 4. Install & configure Samba + SSH (+ Docker). Prompts for a Samba password.
sudo ./setup.sh
# skip Docker for now: sudo ./setup.sh --no-docker
# also set up remote access: sudo ./setup.sh --with-tailscale
# (flags combine, any order: sudo ./setup.sh --no-docker --with-tailscale)format-drive.sh refuses to run without an explicit device, refuses to touch the
disk your OS boots from, and makes you re-type the device path to confirm.
- NAS from macOS: Finder →
Cmd-K→smb://<pi-ip>/storage, log in as your Samba user. The config includes macOS interop, so Finder behaves well. - Coding:
ssh <user>@<pi-ip>, or in VS Code use Remote-SSH to open the Pi as a folder — files and compute stay on the Pi, editor stays on your Mac. Remote-SSH installs nothing extra: VS Code auto-manages a small server component on the Pi the first time you connect. (A browser IDE — code-server — is a separate, heavier option you'd run as a container; skip it on a Pi 3.) - Fixed address: set a DHCP reservation for the Pi in your router so its IP (or hostname) never changes.
- Remote access (optional): run
setup.sh --with-tailscale(or install Tailscale manually) on the Pi, plus the app on your Mac/phone with the same login. You then reach both services from anywhere via the Pi's Tailscale IP — no ports opened, all traffic end-to-end encrypted. Never expose SMB to the public internet; Tailscale is how you reach it remotely.
This is a single drive, so it has no redundancy. Redundancy only survives a dead
disk anyway; a backup also survives deletion and corruption. Once you have a
second drive or a mounted target, set BACKUP_DEST in config.env and run:
sudo ./scripts/backup.shIt mirrors your data there with rsync, and refuses to run if the source drive
is unmounted (which would otherwise wipe the backup). Schedule it weekly via a
root cron job — the header of backup.sh has the exact line. For off-site or
encrypted/deduplicated backups later, restic or rclone are the next step up.
Edit docker/docker-compose.yml (a ready Home Assistant block is there,
commented out), then:
cd docker
docker compose up -dPoint each service's data at /srv/storage/appdata/<service> so it travels with
the drive on your next upgrade.
config.env # your settings (safe to commit)
setup.sh # installs & configures Samba + SSH + Docker
scripts/format-drive.sh # *** destructive *** one-time drive format
scripts/mount-drive.sh # fstab mount by UUID (non-destructive)
scripts/backup.sh # rsync mirror to BACKUP_DEST (guarded)
scripts/lib.sh # shared helpers
config/smb.conf.template # Samba config (filled in by setup.sh)
docker/docker-compose.yml# future add-ons