π Introduction
Over the past few weeks, I went on a home-lab adventure to consolidate my self-hosted services:
As most of you self-hosters would know, if you tweek one thing, something else is bound to break. 😅
- OpenMediaVault (OMV) for storage & Docker management
- Immich for private photo backup
- Jellyfin for private home video backup
- AdGuard Home for DNS-level ad blocking
- Hugo Blog to document my journey
- Tailscale for private remote access
- Cloudflare Tunnel for secure public access without port forwarding
- Uptime-Kuma for monitoring all the services that I am hosting
After a few networking headaches and some Docker/NAT surprises, I now have a stable hybrid setup where:
- I can upload photos privately via Tailscale to Immich
- I can upload home videos privately via Tailscale and Syncthing to Jellyfin
- I can access OMV and AdGuard securely from anywhere via Cloudflare
- My blog is public on a custom domain with HTTPS
- No ports are exposed to the internet directly
- I can monitor all service using uptime-kuma
This post documents the final working configuration.
π₯οΈ Hardware & Base Setup
- Raspberry Pi 5 (8GB)
- NVMe SSD mounted for OMV and Docker storage
- OpenMediaVault (OMV) as the host OS
- Docker & Portainer for container management
I installed these services via Docker Compose:
- Hugo Blog β
- Portainer
Then I installed the following using Portainer:
- AdGuard Home π‘οΈ
- Immich (Server, DB, Redis, ML) and Jellyfin as a single stack πΈ
- Uptime-Kuma β±οΈ
- Syncthing π
π Networking Architecture
I wanted public access for some services but private uploads for Immich and Jellyfin.
Hereβs the architecture:
βββββββββββββββββ
β Internet β
ββββββββ¬βββββββββ
β
Cloudflare Tunnel
β
ββββββββββββ΄ββββββββββββββββββββββββββββ
β Raspberry Pi 5 (OMV) β
β β
β - Hugo (Public Blog) β
β β mydomain.com β
β - AdGuard (DNS) β
β β adguard.mydomain.com β
β - OMV (Web UI) β
β β via Tailscale or optional tunnel β
β - Immich (Uploads) β
β β via Tailscale MagicDNS β
β β
ββββββββββββββ¬ββββββββββββββββββββββββββ
β
LAN + Tailscale
- Tailscale handles private admin, Immich and Jellyfin uploads
- Cloudflare Tunnel handles public blog and optional subdomains
- No local SSL needed; Cloudflare provides HTTPS
π§ Fixing Immich & Tailscale Access
Initially, Immich logs showed:
Immich Server is listening on http://[::1]:2283
This meant the container was only listening on IPv6 localhost.
LAN, Tailscale, and Cloudflare could not reach it.
Solution: Bind to all interfaces.
immich-server:
image: ghcr.io/immich-app/immich-server:release
container_name: immich_server
ports:
- "2283:2283"
environment:
- IMMICH_SERVER_HOST=0.0.0.0
After restarting the container:
Immich Server is listening on http://0.0.0.0:2283
β Immich uploads via Tailscale MagicDNS now work again.
βοΈ Cloudflare Tunnel Config
I used subdomains for apps that donβt support subpaths:
tunnel: hugo-blog
credentials-file: /home/user/.cloudflared/UUID.json
ingress:
- hostname: adguard.mydomain.com
service: http://127.0.0.1:8081
- hostname: omv.mydomain.com
service: http://127.0.0.1:81
- hostname: im.mydomain.com
service: http://127.0.0.1:2283
- hostname: mydomain.com
service: http://127.0.0.1:8090
- service: http_status:404
Key Points:
- mydomain.com β Hugo Blog
- adguard.mydomain.com β AdGuard Home
- Added im.mydomain.com to access Immich publicly.
β Current Status
- Blog: https://mydomain.com
- AdGuard: https://adguard.mydomain.com
- Immich uploads: via Tailscale http://openmediavault.tailXXXX.ts.net:2283
- OMV Web UI: via LAN or Tailscale :81
- Everything is now fast, secure, and stable.
- Next step: Optional Cloudflare Access for admin panels and public Immich gallery.
If youβre following my journey and setting up something similar, I highly recommend starting private with Tailscale, then adding Cloudflare Tunnel for selective public access.
Overall Architecture
