Escape from Google Photos – PhotoPrism + Resilio Sync selfhosted setup for iOS

Why

If you’re here, you already know why we’re doing this – they capped the “unlimited for free” offer, they can suspend your account for any reason at any time, and your photos are their product. Luckily, great open source/free software applications exist today that can almost bring that sleek and very useful Google Photos experience to our home servers.

PhotoPrism is a truly polished photo library/organization project, with advanced features such as AI-powered facial and object recognition, but it does not come with its own iOS or Android app – so you have to bring something else to your “stack” in order to sync your photo library with your home server. *An additional limiting factor for iOS users is that there are a lot fewer apps for local photo backups/file sharing than for Android.

It’s important to note that at this time, there are no perfect alternatives to the major cloud providers – in fact, due to the subjective nature of selfhosting, “perfect” will likely never exist. What works for me may not work for you, etc. If you are not impressed with PhotoPrism for any reason, you might want to look at Photoview, another promising project with a different set of goals, pros and cons.

For photo syncing, I chose to go with Resilio Sync because it has a pretty decent app for that (note: Resilio is not open-source). It can actually work with your iOS photos without jumping through hoops – something that Syncthing sadly can’t; and Nextcloud seems to experience functionality-breaking bugs every time I even look at it. I’ve been meaning to write down some thoughts on the current setup while I have it – not really a step-by-step guide, just some notes that might help others.


Getting and organizing your photos

You can obtain a full backup of your Google Photos data through Takeout, but you might find that it is disjointed, with images and EXIF data separated – there is a script to help with that (PhotoPrism is supposedly able to work with these separated files, but I fixed mine just in case). You might also want to combine your cloud data with large uncategorized folders of images on your local computer, and there are scripts that can help with that too.

Setup

Installing both PhotoPrism (guide) and Resilio Sync (guide) is really simple – I used Docker Compose to bring both services up, and then proxied them with NGINX. On the iPhone end, I downloaded the Resilio Sync app, and set up a “Camera Backup” folder. You then have to send a share link to your other Sync instance.

For PhotoPrism to work as expected in the browser, you have to enable WebSockets in your NGINX configuration (guide), and you should also define your domain name (see the answer to this issue) if you would like to be able to share links to your images or videos to other people. If you are using uBlock Origin or another adblocker/privacy extension for your browser, you might still be getting that “You are offline” warning, so consider disabling some browser extensions for a test.

Something else that I remember about the initial process of importing my photos to PhotoPrism is that the generated thumbnails were just too large in size, so I advise on lowering the JPEG quality setting to something like 80. This can slightly impact the “AI” functionality of the application (which can be completely disabled if you’re not into that).


One notable downside of Resilio Sync is that it is not a WebDAV server, so it won’t inform PhotoPrism of file changes. You can do this with a cron job, as advised here, and you can optimize it further, as you don’t need to waste CPU on the indexer if there haven’t been any recently changed files:

#!/bin/bash
if [ ! -f /mnt/raid1/lockfile ]; then #add a lockfile check, as indexing can be slow
  if [ "$(find /mnt/raid1/iphone -cmin -10)" ]; then #set this to whatever your cronjob's frequency is
    touch /mnt/raid1/lockfile
    /usr/bin/docker-compose -f /home/$user/photoprism/docker-compose.yml exec -T photoprism photoprism copy
    rm /mnt/raid1/lockfile
  fi
fi

Another downside of Resilio Sync – more specifically, of its iOS app – is that it does not back up your photos automatically, so you have to remember to open the app any time you want to send your pictures to your server (which is usually when you already want them on your PhotoPrism site). There is a paid subscription that adds this option as well as some other functionality, but you could also work around this by setting up an Automation in the Apple Shortcuts app – you can then automatically open the Sync app every time you connect to your home network, or every time you close the Camera app, or every night at 3AM. I personally use the “when connected to power” condition and it works just fine.

A third downside of Resilio Sync, and the one I haven’t worked out yet, is that it preserves the files on your home server even after you’ve deleted them from your phone (which is a whole task on its own). In effect, your Sync directory keeps getting bigger and bigger, and the PhotoPrism indexer checks if each file is already added to your database, wasting both real and CPU minutes in the process. Deleting files from the filesystem without informing the Resilio Sync service results in a “corrupted” share, so the only way around this seems to be to manually delete and re-create your share across every device. That’s a bit of a hassle, but it doesn’t have to happen that often, so I can live with it for now. The ability to work around this may be another one of Resilio Sync’s paid features, I’m not really sure.

Last but not least, Resilio Sync does not seem to be actively developed any more – in time, this will become more of an issue, especially for the mobile app.


I’ve been using this setup for a while, and I’m pretty happy with it. I also keep my Google Photos app and account, mostly as a backup. If something changes in this setup or if I come up with any improvements, I’ll be updating this post.

Limit your sites to VPN-connected (*and local) clients only

The problem: You have a bunch of sites running behind an nginx proxy, and you want to limit access to some of the more critical ones to your VPN clients only as a second security layer. You also have an ISP that will sometimes change your public IP address, so you are using the services of a dynamic DNS provider such as DuckDNS.

The solution: YMMV, but generally, web requests from your VPN clients will originate from the public IP of your VPN server. Well that’s handy – you only have to tell nginx to allow anything from this IP, right?

Not so fast: nginx has no idea of its own public IP address. There are nginx modules that will do a reverse DNS lookup on a hostname (like the one provided by your dynamic DNS provider), but as this has to happen on every request, the performance hit will be substantial.

However, you already have a cron job running every once in a while informing DuckDNS of your current IP address – you simply have to add a couple of lines to your script so that it informs nginx, too! Here’s an example of that addition:

resolved_ip=$(dig a your-subdomain-here.duckdns.org +short);
if [ -n "$resolved_ip" ]; then
    echo "allow $resolved_ip;" > /path/to/duck.conf;
fi

Test your script – duck.conf should now contain a single allow directive with your public IP. You can safely include it in the configuration file for the site you’d like to limit – under the server, location or http block:

include /path/to/duck.conf;
deny all;

Restart nginx and test accessing with and without VPN.

edit: It hadn’t occurred to me originally, but this will also cover any clients from your local network too (since they are also behind the same public IP) – unless they access the sites via IP instead of domain name. This may or may not be an added benefit.
edit 2: Encased that echo in an if block because saving an empty $resolved_ip (say, if your Internet connection is temporary down) will bork your whole nginx on the next restart.

Digital Life Redundancy

This is a belated response to the “I’ve locked myself out of my digital life” post that was circulating earlier this month. Its author describes a hypothetical situation in which a single unexpected disaster (such as a lightning strike) could cause disruptive damage to your digital – and in turn, to your “analogue” – footprint.

The post is thoughtful and makes a good point – I’m not really arguing against it. In fact, I believe that everyone should be doing some sort of prep work for unforeseen events. However, a lot of the theoretical aftermath can be avoided with this one simple trick: redundancy. Keep a spare work laptop* at your office or in your parents’ house, use a password manager with a secure online repository (such as pass with git), and you’ll be fine until Russia invades. Plus, that’s a little less dead weight to lug around when biking to work.

* – five-year old Thinkpads and Latitudes go for around 250 EUR, but you can run Firefox and Vim on a 10 year old X220 anyway

Openbox scaling for HiDPI displays

I recently switched to a laptop with a 16:10 QHD display (popularized as Retina by a certain shoddy company), and I faced a problem that I had been fearing for a while – scaling on X11/Openbox. Specifically, I really didn’t want to switch to anything Wayland, GNOME and/or KDE related – not because I have anything against them, but because I’m too old and don’t want to. I have been using Openbox (and some version of the same configuration files) for 10+ years, longer than my current Linux distribution, so you can say that there is some attachment involved.

Searching online does not yield any helpful guides or tips, and while there is a detailed article on HiDPI displays on the Arch Wiki, it doesn’t go into any detail on the less popular window managers out there. There is, in fact, a singular grain of gold, but it is well hidden under a section cryptically titled “X Resources”:

If you are not using a desktop environment such as KDE, Xfce, or other that manipulates the X settings for you, you can set the desired DPI setting manually via the Xft.dpi variable in Xresources:

Xft.dpi: 192

After about half an hour of messing around with fonts for individual applications, it turned out that this was the only setting that had to be changed. Of course, there will always be that app that does not care about your system-wide configuration (looking at you, tint2), but 95% of the work was handled by that change.

Seen, read 2021

Inspired by Soderbergh’s annual list of “seen, read,” and with the help of almost two full months of journaling (sigh), Goodreads and the Netflix’ viewing history (there will be some misplaced but memorable mentions at the end):

All caps, bold: MOVIE, All caps: TV SERIES, Italics: Book, Quotation marks: “Video game”

Disclaimer: a lot of the movies are rewatches and a lot of the books are rereads (signified with * when remembered) but worst of all, a lot of the shows are just really crappy dating reality TV.

January
1.1 – JOHNNY MNEMONIC* (1995), BLACK SPOT S01 (dnf)
2.1 – VIRGIN RIVER S01 (a very “hallmark movie” series)
4.1 – VIRGIN RIVER S01 (dnf)
5.1 – ‘watched first episodes of really bad shows’ (author doesn’t say which ones)
10.1 – finished S01 of THE EXPANSE
12.1 – Всеобща история на безчестието
14.1 – The League of Extraordinary Gentlemen, Vol. 1 & Vol. 2
17.1 – finished “Overcooked”
23.1 – “Shadow of the Colossus” – beat two colossi
24.1 – ‘bought new PC in a shady parking lot’ (lol)
27.1 – finished “Sleeping Dogs”, S04 of THE EXPANSE

February
1.2 – ‘jammed with Valyo – wrote a song!’
4.2 – finished S05 of THE EXPANSE
10.2 – started reading about PROLOG
13-14.2 – ‘extended “Stardew Valley” weekend’, PROMETHEUS* (2012), ‘spoke w/ dad’
15.2 – finished S02 of MANDALORIAN
16.2 – DUNGEONS AND DRAGONS (2000) (with the excellent RLM commentary)
18.2 – finished S01 of ARE YOU THE ONE; some TNG S03
19.2 – finished S02 of ARE YOU THE ONE
20-21.2 – “Stardew Valley” weekend, COLLATERAL* (2004)

March
2-6.3 – THE CIRCLE S01
17.3 – V for Vendetta
19.3 – V FOR VENDETTA* (2005)
26.3 – Swamp Thing, Vol. 1: Saga of the Swamp Thing

April
6.4 – THE SERPENT (dnf)
13.4 – TNG S03
26-28.4 – THE CIRCLE S02

May
7.5 – MISSION: IMPOSSIBLE – ROGUE NATION* (2015)
14.5 – TNG S03
15.5 – WOMAN IN THE WINDOW (2021), STOWAWAY (2021)
16-17.5 – TNG S03
18-21.5 – THE CIRCLE S02, TNG S03, ARMY OF THE DEAD (2021)
23-27.5 – THE CIRCLE FRANCE

June
2.6 – TNG S03 (finished)
4-12.6 – TNG S04
26.6 – THE CHRONICLES OF RIDDICK* (2004)
27-28.6 – TNG S04

July
3-7.7 – TNG S04
12.7 – INFERNAL AFFAIRS (2002)
15-22.7 – TNG S04 (finished)
23-27.7 – TNG S05
28.7 – LOVE IS BLIND S01 AFTER THE ALTAR
29-31.7 – TNG S05

August
5.8 – Философията. Едно много кратко въведение (did not finish – horrible automated translation)
17.8 – Snow Crash
23.8 – Farewell, My Lovely
1-23.8 – TNG S05 (finished)
26.8 – The Lady in the Lake
24-30.8 – TNG S06

September
5.9 – SE7EN* (1995)
6.9 – TNG S06
8-11.9 – THE CIRCLE S03
13.9 – KATE (2021), The Big Sleep
15.9 – THE CIRCLE S03
17.9 – THE GAME* (1997)
19.9 – TNG S06
20.9 – CLICKBAIT
21-24.9 – THE CIRCLE S03
24-25.9 – CLICKBAIT
26-29.9 – MIDNIGHT MASS
29.9 – THE CIRCLE S03 (finished)
30.9 – MIDNIGHT MASS, BRITNEY VS SPEARS (2021)

October
2.10 – DEATH BECOMES HER (1992)
4.10 – MIDNIGHT MASS (finished)
5.10 – THE GUILTY (2021)
6.10 – THE FALL S01 (dnf)
6-7.10 – MARCELLA S01
8.10 – NORM MACDONALD HAS A SHOW
10.10 – MARCELLA S01 (finished)
11.10 – MARCELLA S02 (dnf), Batman: The Man Who Laughs
13.10 – HUSH (2016)
14-17.10 – DIRTY JOHN S01 (finished)
17.10 – GHOSTBUSTERS 2* (1989) (dnf)
19-20.10 – MY NAME (finished)
20.10 – AD ASTRA (2019) (dnf), SCARY MOVIE 2* (2001) (dnf), EXTRACTION (2020), TNG S06
21.10 – THE TRIP (2021), TNG S06
26.10 – THE BIG SLEEP (1946)
26-28.10 – COWBOY BEBOP

November
2.11 – CRIMSON FORCE (2005)
4.11 – HYPNOTIC (2021)
9.11 – Virtual Light
11.11 – LOVE HARD (2021) (recognized Nina Dobrev after only knowing her from THE Vampire Diaries Video, which is much better than this movie btw)
12.11 – RED NOTICE (2021)
16.11 – Идору*
15-17.11 – MIDNIGHT DINER S01
18-19.11 – BEHIND HER EYES (dnf)
20-21.11 – COWBOY BEBOP (live action)
21-22.11 – HYPERDRIVE (dnf)
22.11 – COWBOY BEBOP (live action, dnf)
27.11 – SELLING SUNSET S04 (dnf), BRUISED (2020)
29.11 – TNG S06
30.11 – MIDNIGHT DINER S01

December
1.12 – MIDNIGHT DINER S01
3.12 – L.A. CONFIDENTIAL* (1997)
4-5.12 – LOCKE & KEY S01 (dnf)
6.12 – CHINATOWN* (1974)
7.12 – All Tomorrow’s Parties
8.12 – THE PELICAN BRIEF* (1993)
8.12 – GONE BABY GONE* (2007)
19.12 – SELLING TAMPA S01 (dnf)
24(?).12 – ROAD HOUSE (1989)
28-29.12 – THE SILENT SEA S01 (dnf)

Various media that cannot be accurately dated

  • Saw DUNE (2021) in IMAX (likely in November).
  • Saw MANHUNTER (1986), THIEF (1981), SONATINE (1993), VIOLENT COP (1989), HARD BOILED* (1992), BOSS LEVEL (2020), THE TOMORROW WAR (2021), NO TIME TO DIE (2021), AMERICAN MOVIE (1999) & probably others at home. I am pretty sure that we also saw all MISSION: IMPOSSIBLE* movies this year, but Netflix only remembers the last one.
  • Video games: played a bunch of “Chrono Trigger” (SNES), “Parasite Eve” (PS1), “The Last Of Us Remastered” (PS4), “Horizon: Zero Dawn” (PS4), “TIS-100” (PC), but did not finish (would love to, eventually). Played and finished “GTA V” (PS4), “C&C: Red Alert 2″* (PC), “Grim Fandango Remastered” (PC), “The Curse of Monkey Island”* (PC) and “Beneath a Steel Sky” (PC). Spent a lot of hours in “Stardew Valley” (PC, PS4) and “Gang Beasts” (PS4) with lovely friends.
  • Board games: got to play one! session of “Symbaroum” RPG live and in person, and got in a few “regular” board games as well (notably: Concordia, Root, one game of Oath).
  • Started watching SUCCESSION in December, and have probably watched at least some of SOPRANOS S03 & S04 throughout the year, but unlike Netflix, HBO GO doesn’t keep a detailed viewing history.
  • I will likely keep adding to this list as I (mis)remember more.