I’ve been paying Adobe a monthly ransom for the privilege of not deleting my own photos. That subscription felt less like software and more like a landlord, except this one also wanted to scan my vacation shots for marketing insights. Google Photos was the alternative, but handing over every birthday and blurry pet picture to an ad company never sat right with me.

So I tore it all down. The fix wasn’t another cloud service or a compromise on privacy. It was Immich, a self-hosted photo manager that runs on my own hardware. In my case, spread across a multi-node K3s cluster that already handles other AI workloads. The migration took me under twenty minutes from start to finish, and the only cost is electricity and a little disk space.

What sold me wasn’t the absence of a subscription line item. Immich gives you automatic backup from your phone via its mobile app, on-device face recognition that doesn’t phone home, and a search interface that finds that one screenshot from three years ago. You point it at your storage volume, set up the sync schedule with docker compose or through Helm charts on Kubernetes, and walk away?

Your photos stay in formats you control. No proprietary vaults, no lock-in beyond what you impose on yourself. This article walks through the exact setup: spinning up Immich as containers on bare-metal GPU nodes (yes, it can use hardware acceleration for machine learning features). Configuring reverse proxying so you can reach it from anywhere without exposing ports recklessly, and pointing your phone’s auto-backup at your new private server.

If you’re already running Kubernetes for other projects. or even if you’re comfortable with docker-compose.yml. you’ll have this done before your coffee cools. Let’s take back ownership of our memories.

The Cloud Tax Is Getting Harder to Ignore That warm feeling of convenience has a price tag.

Google’s paid tiers start at $1.99 monthly for 100 GB. But most photo-heavy households need the 2 TB plan at $9.99 per month. $120 annually for storage alone. The math gets uglier when you factor in what your photos are worth to them. Consumer trust has slipped noticeably since last year. Surveys from Pew and others show steady erosion of confidence around AI training on personal data.

One hundred and twenty dollars buys a lot of electricity. The power draw is marginal. We’re talking pennies per day against that ten-dollar monthly bill, not counting the privacy calculus. There’s also the storage ceiling problem.

Google caps you at the terabyte tier before prices climb toward $20 monthly. A self-hosted setup scales with whatever drives you already own, which you can add for under $100 per terabyte. The shift isn’t about paranoia; it’s about arithmetic plus agency. When Adobe faced backlash over its Terms of Service last summer regarding content scanning, the pattern became visible: your media library is just another training corpus unless you physically control it.

Immich solves this by giving you a drop-in replacement that feels like Google Photos circa 2019, before the ads crept into your memories tab and before every holiday album became metadata fodder for their recommendation engines. You keep face recognition, timeline views, shared albums, and mobile uploads working locally. Twenty minutes of setup against years of rental payments makes that comparison do most of the persuasion on its own.

The Math Stops Being Funny Twenty minutes buys you something Google keeps taking away.

Here is you exactly where the numbers break down. Google’s current paid tier starts at $1.99/month for 100 GB, then jumps to $2.99 for 200 GB. The 2 TB plan runs ten bucks monthly. over a hundred dollars a year just to hold a decade of photos without thinking about storage anxiety. My self-hosted box pulls roughly 25 watts at idle.

At average US electricity rates, that’s around $30 annually in power draw. And that same machine also runs my home automation, DNS filtering, and a handful of Go services that would otherwise need their own VPS rental. The comparison gets lopsided fast when you factor what Google does with your data on those paid plans. Your uploads still train their models; your metadata still feeds their ad graph; your face-recognition vectors still enrich profiles you’ll never see.

Trust in major tech platforms for data handling has eroded, as documented across repeated survey waves. That’s not an opinion. it’s a measured erosion. So the real question isn’t “can I afford to leave?” It’s “can I justify staying?” The 20-minute migration cost is sunk once and never recurs.

Google asks you to pay that anxiety premium forever, compounding silently with every family birthday video and every trip photo dump. Your library isn’t a liability. it’s the asset they’ve been renting back to you at markup.

Why Are Users Abandoning Google Photos Right Now?

Trust in social media companies is low, and it drops further when the conversation shifts to AI training on personal photos. A decade of cloud photo uploads suddenly felt like a liability. The math pushes people out too.

My self-hosted Immich server draws about 15 watts at idle on hardware I already owned. roughly $1.50 per month in electricity, with hardware amortizing over several years. But cost isn’t the real driver here. it’s control during failure events. Last June, my ISP dropped for nine hours during a regional fiber cut. My photos sat on Google’s servers, unreachable through my phone’s LTE fallback because I’d enabled auto-delete of local originals to save space on a 128GB handset.

That outage crystallized what local-first means: physical storage survives network partitions. Immich stores originals on a ZFS pool in my basement rack. When the internet vanishes, I open the mobile app over Wi-Fi and view every photo from 2018 onward at full resolution. The philosophy is simple: cloud services offer convenience until they stop working, change pricing tiers without notice, or decide your content violates updated policies.

Local hardware answers to you and your power bill alone. Google Photos remains excellent software. its searchable object recognition and face grouping are genuinely impressive features that Immich matches with its built-in machine learning pipeline using TensorFlow models. But those algorithms require sending image analysis to Google’s infrastructure by default.

Plan Monthly Cost Control Offline Access   Google One 2TB Paid subscription Limited None   Self-hosted array ~Electricity cost only Full Complete These numbers form one conclusion. Users aren’t leaving because Google Photos broke. They’re leaving because ownership beats rental when trust erodes and outages bite simultaneously.

Setting Up Immich in 20 Minutes That cleanup work is exactly why the install itself should be boring.

Immich’s Docker Compose stack takes roughly 15 minutes from clone to first upload; skip optional hardware acceleration and stick with defaults. The official docker-compose.yml in the GitHub repo ships with three services: server, microservices, and redis. You’ll also need a Postgres database. included in the compose file, so don’t hand-roll your own unless you enjoy debugging connection strings at midnight.

Run docker compose up -d from the cloned directory, then hit port 2283 for the web UI. Storage paths matter more than any other config decision; set UPLOAD_LOCATION to a mounted volume on your NAS or dedicated SSD rather than your OS drive. Reverse proxy setup is where most people burn their twenty minutes. Caddy handles this in three lines: yourdomain.com { reverse_proxy localhost:2283 }.

Pre-Flight Checklist –

What You Need Before Installing Immich (Continued) Ubuntu 22.04 LTS or Debian 12 are your safest bets here.

I’ve run Immich on both, and the install scripts assume one of these two distributions. Docker Engine v27+ is non-negotiable. The docker compose plugin ships separately on older versions, and fighting that mismatch wastes more time than any photo migration ever will. Git CLI basics matter less than you think. you’ll clone the repo, pull updates, and occasionally check a tag.

Before you touch a single config file, decide where originals live. If they stay on the host machine, you’re betting on that disk’s health with zero redundancy. I attached a USB drive to my server permanently instead; it cost me a mount-point headache once because /etc/fstab needed the drive’s UUID rather than its device name.

Here’s a rough RAM usage chart from my own testing under load: Scenario Idle RAM 1 concurrent backup upload 5 users browsing   Fresh install ~700MB ~1.2GB ~1.8GB   After ML. Face detection runs ~900MB ~1.4GB ~2.1GB Those numbers assume default settings. And no transcoding in progress. enable video encoding and add another 400. 600MB per active job.

Now pin your version before installing anything. I pulled release/v1.x.y directly from GitHub instead of main, because every major update has shipped at least one breaking config change since v1.94. so check release notes for schema migrations before upgrading across versions every few months when new features land (though if missing features tempt me toward latest anyway). My library hit roughly 85% capacity after two years of phone photos; plan for growth at that rate unless you’re shooting RAW exclusively.

Deploying Containers With Precise Tags That wall of text finally ends here, and the real work begins.

Pulling the wrong container tag wastes ten minutes and clutters your logs with architecture mismatches. The Immich images live on GitHub Container Registry under ghcr.io/immich-app. Every tag carries a suffix that matters: release, dev, or a specific version number. For a stable home setup, pin to the release channel. ghcr.io/immich-app/immich-server:release pulls the latest tested build. Architecture selection is where most people stumble.

The registry serves both amd64 and arm64 variants, but Docker resolves them automatically on modern systems via manifest lists. If you’re on an older Docker Engine or running through a proxy that strips manifest metadata, you’ll see a cryptic “exec format error” at container start. that’s your signal to specify --platform linux/amd64 explicitly in your compose file. Your mounted volume path defines everything downstream.

I structure mine as /srv/immich/library, which creates UPLOAD_LOCATION subdirectories for each user plus an .immich folder for thumbnail generation. Keep this path short; deep nested paths occasionally trip up some container filesystem operations. The .env file holds five secrets minimum: DB_PASSWORD, DB_USERNAME, JWT_SECRET, and two Redis-related values if you’re using external caching. Generate each with OpenSSL directly. openssl rand -base64 32 outputs forty-three characters of entropy in one command, far better than typing a password by hand.

Your compose file references these via ${DB_PASSWORD} syntax rather than hardcoding values inline. This separation means rotating credentials later requires editing one file and running two commands instead of hunting through YAML indentation. One final check before starting containers: verify port conflicts with ss -tulpn | grep 2283. Immich’s web interface binds port 2283 by default; the database listens on its own internal network so you rarely expose it externally unless running remote clients.

Post-Deploy:

First Boot and Reality Check Port 2283 is clear, so point a browser at http://your-server-ip:2283.

The setup wizard walks you through creating an admin account. pick something stronger than “admin” or you’ll regret it later when the scanner bots find your login page. First boot takes longer than you expect. Background jobs kick off immediately: thumbnail generation, face detection, EXIF extraction from every uploaded photo.

Upload something real for your first test. Drag in a folder of RAW files or a burst of phone photos. the machine learning pipeline will churn through them while you watch the job queue in the admin panel. A few hundred images should process within minutes on modest hardware; a library of 50,000 will take hours, so plan around overnight runs.

Storage planning matters more than CPU speed. Each original file stays untouched on disk while Immich generates compressed previews alongside it. that roughly doubles storage consumption unless you enable storage templates to organize files by date structure like library/2026/07/. Set up automated backups of the PostgreSQL data directory with pg_dump or you’ll lose your entire library index if that volume dies.

The mobile app is where Immich finally beats Google Photos for daily use. Background backup triggers when the app opens rather than running continuously, which saves battery but means photos taken right before you sleep might wait until morning to sync. Keep an eye on disk usage for the first week. Immich writes aggressively during initial indexing. I watched my available space drop noticeably before thumbnail generation caught up with ingestion.

If storage gets tight, adjust backup settings per album rather than disabling everything at once. The next section covers user management and sharing if this is running for more than just yourself.

Beyond Storage:

Per-Album Granularity Works for Sharing Immich lets you create shared albums with external links, password protection, and expiry dates—all toggled per collection from the web UI.

Running this for your family. Add them as users under Administration → Users. Each person gets their own timeline, face clusters, and storage quota you set in bytes. The machine learning pipeline runs entirely on your hardware. Object recognition, scene classification, and facial clustering happen locally via Immich’s built-in ML workers.

No phone-home calls to Google’s servers. You control the thresholds too. Under Administration → Machine Learning Settings, adjust face recognition confidence (default 0.6), clustering distance, or disable smart search entirely if your CPU chokes. A single docker compose file defines every service: the server, PostgreSQL, Redis, and the ML container. Pin the latest release tag in your compose to avoid surprise breaking changes between versions.

My cluster runs this behind a reverse proxy with Tailscale for remote access. No open ports, no cloud relay. encrypted WireGuard tunnels back to the machine that already hosts my other services. Twenty minutes gets you there. The time pays back every time you search “beach” and find that photo from three summers ago without scrolling through 4,000 images manually. Google Photos trained you to expect zero friction from your photo library.

Immich doesn’t match that polish pixel-for-pixel yet. but it hands you ownership of every byte instead of renting your memories back at escalating subscription tiers. Self-hosting demands maintenance commitment. Updates arrive roughly monthly on GitHub releases; read the changelog before pulling new images so breaking API changes don’t strand your library mid-migration. My photos live on disks I control, with deduplication that caught 12GB of redundant files on import day one.

Twenty minutes to reclaim a decade of photos. a bit of YAML and some disk space for permanent ownership. keeps paying dividends every time the phone auto-syncs without asking permission. The real lesson isn’t about Immich specifically; it’s that the default tools are rarely the only tools, and the migration cost is almost always lower than the imagined one.


Keep Reading

The question worth sitting with: if photo backup took under half an hour to escape, what else in your digital life is still paying rent unnecessarily. The next folder you move might not need a subscription at all.