Skip to content
Saim Hashmi

VaultPi

2025-09

My Raspberry Pi homelab and personal cloud: an NAS provisioned with IaC, an event-driven AWS photo pipeline, and zero inbound ports. This site is served from it.

VaultPi homelab rack
ROLE
Solo: design, infrastructure as code, pipeline and ops
RESULT
Replaced iCloud for 14,000+ photos (300+ GB); reproducible from Terraform + 11 Ansible roles
STATUS
Running 24/7 (Sep 2025 – present)
STACK
AWSTerraformAnsibleDockerImmichTailscaleCloudflare Tunnel

Quest briefing

Problem

Photos and files piled up on my laptop with no dedup, no tags and no backup. A Raspberry Pi already ran my personal services and sat mostly idle. I wanted a personal cloud I control, without opening my home network to the internet.

Raspberry Pi in its case, cabled up next to a keyboard

What I built

  • Reproducible from code. The Pi is provisioned with Ansible (11 roles): Docker, HDD mount, Samba, Immich, n8n, Tailscale, monitoring, Watchtower and more. The AWS side (IAM, S3, DynamoDB, Lambda) is managed with Terraform.
  • Event-driven AI photo pipeline. Photos sync from the local HDD to S3. Each upload triggers a Lambda that tags the photo (Rekognition) and embeds it (AWS Bedrock Titan), storing results in DynamoDB. Scripts built on top handle duplicate detection and text-to-photo search.
  • Immich is the self-hosted photo app. It replaces iCloud with no storage cap or subscription.
  • Zero inbound ports. Public services, including this portfolio, are exposed only through outbound Cloudflare Tunnels. Private services (the NAS, Immich, Grafana) are reachable only on the LAN or over Tailscale. Samba never touches the tunnel.
  • Ops. Containers run under Portainer. Watchtower keeps images updated and posts to a Discord channel. Grafana dashboards cover the Pi, each container and the AWS side, with disk alerts before a mount fills.

Cloudflare Tunnel request flow: browser to Cloudflare's edge to the tunnel daemon on the server (diagram from Cloudflare's docs)

Portainer container list for the services running on the Pi

Discord channel receiving Watchtower container-update notifications

A GitHub Actions build-and-push Docker job next to Watchtower logs on the Pi

Terminal session with lazygit and Portainer server logs on the Pi

Result

  • 14,000+ photos (300+ GB) moved off iCloud onto hardware I own.
  • 0 inbound ports open on the home network. You are reading this page through the same tunnel.
  • The whole box can be rebuilt from the repo instead of from memory.

Loot (what was learned)

  • Dedup exposed organizational debt, not just stray copies. Most duplicates were whole folders mirrored into each other.
  • Raw object counts lie. Filter to the files you'll actually process before sizing the job.
  • Outbound-only tunnels remove a whole class of attack surface.
  • ARM quirks: not every container image runs well on a Raspberry Pi.
  • DNS is always the problem. Even at home. Especially at home.

That's the whole story.

Liked it?

Say hi, I'm open to new-grad roles from summer 2027.

Something off?

Tell me and it gets fixed tonight.

← All work