Google One reminded me to pay next year’s bill, which also reminded me that all of my precious photos are at Google’s mercy. Over the last two decades, I’ve switched devices multiple times while trusting Google to maintain my full photo archive (worse, I didn’t even have a local full copy).

Now I no longer want to place this trust in Google. It has long ceased to be a “Don’t be evil” company (e.g., Google Maps recently chose to follow the Trump administration’s renaming of Lake Ontario as Lake America). The worry about my photos disappearing from Google Photos is not unfounded. There are user reports claiming their accounts were disabled due to false positives from automatic flagging systems (e.g., case 1 and case 2). Data privacy is also a concern. Per Google’s policy, “We don’t train any generative AI models outside of Google Photos with your personal data in Google Photos” (emphasis mine). This does not rule out training models used within Google Photos. The same policy also says that, when Photos is connected to other services, Google may train on summaries, inferences, and generated media based on the contents of a Google Photos library.

So I shopped around for a self-hosting solution. My criteria are simple: nice UX, basic ML features for photo analysis/search, a professionally maintained open-source codebase, and a healthy funding model likely to sustain future development. Immich turns out to be a good fit.

I bought a used Dell OptiPlex 7060 mini PC with 16GiB RAM for less than CA$300 to host Immich. I exposed the Immich instance to the Internet via a custom domain that resolves to a VPS connected to the mini PC at my home via WireGuard. Installing Immich was super easy: starting a few Docker containers was sufficient. I used OVHcloud to host the Internet-facing VPS and object storage for data backups with rustic. OVHcloud is a French cloud provider, operates data centers in Canada, and provides a simpler, more intuitive UI than AWS.

The migration process was mostly fine. I used Google Takeout to download my photo library. I then used immich-go to import it into Immich. There were two issues. The first was that Immich’s CUDA OCR worker seems to have a memory leak, which I bypassed by setting up a health check to restart the container once GPU memory usage passes a certain threshold. The other issue was that some files in my Google Takeout archive had JPEG content despite their .HEIC extension, which confused Immich. I submitted a patch to immich-go as a workaround.

The user experience with Immich is better than with Google Photos. Since all data are served from NVMe storage over my own network, the UI feels blazingly fast. I can scroll to any date and the photos and videos just load in a fraction of a second, while Google Photos always felt sluggish. ML features like semantic search, face recognition, and OCR also work well enough. I don’t have any complaints so far. If you find Immich helpful and can afford it, please consider supporting the project by purchasing Immich.

The final task was to reduce my Google One storage usage so I would no longer need a paid subscription. As you have guessed, Google does not provide an easy method to batch-delete photos. In the end, I tasked GPT 5.6 with writing a few scripts using Playwright to delete all of my photos through the UI. I still needed a few iterations to get them working correctly, so here they are if you want to use them: login.py for starting the system-installed Chromium to sign in to Google Photos (because Google rejected login attempts through Playwright’s embedded Chromium), and remove_all.py to remove all the photos. WARNING: This script deletes all photos and videos and empties the entire Trash, permanently erasing its contents.