Self-hosting Immich – A Google Photo Alternative or an Erotic journey to the Abyss?

Created on: 26 Oct 23 01:08 +0700 by Son Nguyen Hoang in English

Access your selfhosted service using Cloudflare & Tailscale

Alt text

Once upon a time, there was a man who believed in the power of authority and unity. This man sold his soul to the-god-whose-name-should-not-be-spoken, in favor of a small gift: the power of The Great Knowledge – in which the man now can have his own Book Library, that can be compared to the great Library of Alexandria, which had been burned not once, not twice, but at least three times. Granted the power of wisdom and knowledge, but the man wants more. He asked God for more power. The Lord said:

– I have granted you the power of Knowledge. Now, today what do you desire?

The man replied:

– The power of memory. Do you have it?

– I am the lord of all lords. I can grant you whatever you wish. But as always, there is a cost.

– What cost? Memory is fucking cheap! I already sold you my soul. What do you want now?

– Soul – What is that? There is no meaning in gathering souls. The universe had long gone to the phase of technocrat and capitalist realism! Your soul is no longer valued here!”

– Then what do you want?

The mighty god replied:

45,000 VNĐ per month for 100 GB

The man collapsed! His eyes got blurred! His heart felt like a thousand-ton pressure! The price was not much. But he felt betrayed, his god had become greedy. His source of meaning got emptied and then in his heart there was nothing but void – the pure void. An existential crisis crept in and thus, he soon found himself exiled from heaven!

The betrayal turned his heart black. His eyes grew red as he could see no light and unity. Heaven banished him, thus pushing him to the road of darkness. The man became thirsty for power, depraved art, and dark magic. As he became more lustful and no longer believed in God, he entered the realm of darkness and traveled on the role of Ubermensch!

On the road, there was a tavern for the wanderer like him. A small place for those exiled from God. The pub’s name was carved in wood, the name was simple but powerful like an ancient rune of the Elder Gods:



  • My Google Photo got full. My data exceeded the free tier (15 GB)
  • I have so many images & videos and I want a service to manage them all.
  • I refused to pay for more services.
  • Who the hell knows what Google Photo can do with my images?

So, I decided to deploy my own solution. Why I prefer the self-hosted method:

  • I simply enjoy the process of deploying services.
  • There are plenty of self-hosted services for photos & videos. I used Immich because this one is heavily under development. The community (r/selfhosted) also loves the app.
  • I have a spare laptop that is more than enough to host the server.
  • Learning more is great.

Alt text

Deploy Immich in Local Machine
Local Machine Information
  • OS: Linux Mint
  • Ram: 12 GB
  • Very simple, just follow the docs
  • You are recommended to use docker: docker-compose and docker-desktop (optional)
  • Visit the app at default port: localhost:2283

But here comes the first problem:

How to make the server public?

The bar was filled with nasty thugs. There were the outcasts, bandits, thieves, whore, Orcs, trolls and the Succubus. The protangonist’s head got fuzzy from the noise and the drink. “What did they put in my drink?” – the man asked.

– First time here? – The wise bar owner asked. – Another betrayed one?

The man didn’t reply, but his attitude proved the owner to be right.

– That face will get you in trouble, kiddo! You cannot survive more than one month outside with that attitude!

The man got angry. The drink caused his blood hotter and soon his mind begun to lose temper. Fortunately, there is a dark figure appears behind his back. The figure said calmly:

– Be careful and let your staff down.

There is a mystical aura radiating from this figure. Something calm, but dark and mysterious as the deep ocean. The coldness of his eye makes the man calmer and no longer drunk. Who is this guy? The man wondered but he hadn’t said a word.

– I am not a “guy”

The hood revealed, under the cloak was a blue eye shining like an ancient rune. It was a woman but not a commoner. She was the powerful mage of the Abyss, the one whose name had been long forgotten.

– Call me “Abyss”

The powerful wizard stared soullessly at the man. Our poor man felt like his mind was being read like a book!

How To Access the Server Globally?

There are plenty of methods that can be applied. One thing to note that I want to use my spare machine and deploy the app entirely on my home network. However, home IP is never static. Static IP is expensive and used mostly for companies & organizations. So, so how to tweak it around?

Rent a VPS, deploy the server, and open the IP globally.
  • My first VPS didn’t have enough space. Renting a second one costs money. Also, this app requires a lot of space (200 GB or more). This could be expensive!
  • Again, I want to use my spare machine and deploy the app entirely on my home.
Using DDNS: Dynamic Domain Name Service:
  • Dynamic DNS (DDNS) is a service that can automatically update DNS records when an IP address changes.
  • It checks frequently the IP address of the host server (my home) and then updates the DNS records.
  • One example is DuckDNS
Using VPN:
  • Deploy VPN on a VPS, the public IP of the VPN. Redirect the incoming request from the request to the VPN before routing it to the home server.
  • There may be a need for a DDNS in between.
Using Cloudflare Tunnels:
  • Very quick to setup
  • Install the client tunnel on the home server once.
  • Connection to the server can be proxied through https.
  • Cloudflare provides security features (access group control)
Using Tailscale:
  • Very quick to setup
  • Create “virtual lan” for the devices.
  • Require a client installed on each device to connect to the network.

In the final implementation, I used a combination of Cloudflare & Tailscale services.

– How can you be such masterful, yet still be banished from heaven – The man murmured.

The lady replied:

– This was a long story. You see. I was a mortal, bound by lust, greed, and sin. I was no good then. But one day, I saw a man – a beautiful young man. I seduced him but failed. He was already in love with another mortal.

– Then what next?

– I tried to create a love potion, a poem of darkness, a song of forgotten love, … I used everything witchcraft on him. I also used mortal luxury, wealth, authority, fame, and wisdom to make his heart mine. Yet, nothing worked! In desperation, I did one thing despicable, the most forbidden act. I put a portion of my soul into his body. This is forbidden in all realms. Thus, I created a twisted being inside my loved one! You can say that being is my cursed twin! Two souls in one body. This is dangerous, and more than imaginary! But by this little soul of mine hijacked into his body, I could read my loved one’s mind, I felt like we both share a body, lived in the same house, and ate the same food.

– Such a crazy lust!

The grand wizard breathed slowly, then she smiled faintly.

– Yes, but then things failed apart.

Simple Solution: Cloudflare Tunnel

This is surprisingly good. Cloudflare Tunnel works perfectly great in most cases (but not mine, LOL). Indeed, if there is a future project to self-host, I would not hesitate to use Cloudflare Tunnel. What is the benefit:

  • Free Tier allows a lot of features: eg. Cloudflare Tunnel
  • Simple client setup on Linux, Window, Android, and more
  • Free to customize subdomain
  • Apply https on my subdomain
Quick Guide
  • First, you must have a domain name.
  • You can buy one domain from Cloudflare if you already have one, then redirect the nameserver to Cloudflare nameserver. Alt text
  • Once finished, you can customize the DNS table for your domain in Cloudflare. Set up your DNS if needed.
  • Now, here is where the magic happened, using Cloudflare Tunnel. But, hang on, what is that?
Cloudflare Tunnel?
  • Cloudflare Tunnel is software installed on your server side.
  • Once Cloudflare Tunnel has been installed, create a tunnel on the Cloudflare Tunnel Zero Trust. A new Tunnel will appear in Cloudflare Zero Trust/Tunnel Dashboard like this (The image was collected from Google btw):

Alt text

  • Configure the Tunnel, register a host by adding a public hostname, and assign it to a subdomain name.

  • From here, you can redirect that subdomain to your local service (such as localhost:8383). Now, we created a link to that subdomain to your local server!

  • Finish! Now from anywhere in the world, you can visit your server from your subdomain!

The below diagram shows how Cloudflare tunnel works with your subdomain name

Alt text


However, not every server should be made public. We must have some method of authentication to prevent attackers and unwanted guests! Lucky for us, Cloudflare allows us to create an Access Group for the tunnel service. You can create an access group and then assign that group to the Tunnel.

Cloudflare support multiple access control selector. In the image below, the selector was “emails”, which means that the authentication will be done through email verification. Assume that this access group has been set up on your Tunnel. Once the website is visited, Cloudflare will redirect to an authentication page (prebuilt by Cloudflare), then send a code to your email (your defined mail in the access group) then ask you to enter the code. If the code is correct, then Cloudflare redirects to your server.

Alt text

Such a convenient! A lovely and low-entry bar solution for those beginners at doing selfhost!

Alt text

Listening to the powerful wizard talking about her mad love. The man suddenly smiled, this reminded him of his greatest lover, his lady of Green, a girl once he thought to be perfect, so innocent yet so monstrous in bed! So smart yet so pervert! Such elegant and well-cultured being but at the same time so masterful in the art of seduction!

Such magnificent woman truly exists! There were days he and the girl lying in bed, smiling, and toying before sunrise. Such a beautiful creature, he once thought that moment could last forever! Until it ended. That person with whom he shared so many memories was revealed to be a wicked being! The brown eyes slowly transformed into the coldest glance. The gorgeous body soon became alienated, twisted, and cursed! The slender skin became so rough and corrupted! The woman he loved became less than what he considered a human. Yet still, she was still an angel, but an angel of agony, trickery, betrayal, self-absorbed, and manipulation.

Such a loss!” – He talked to himself – “But is it too difficult to grasp? The most colorful flower only sprung from the most dangerous toxin. Nothing is perfect! There is no charm without some narcissism!”

Problem Arise

Let’s not forget that the original vision was to replace Google Photo with Immich. Normally, the Immich eco-system includes two apps:

  • Immich Server: Where the images are stored.
  • Immich Mobile App (Android/IOS): The client uploads the image from devices to the Immich Server.

Unfortunately, the Cloudflare Solution has two weaknesses:

  • Cloudflare tunnel only allows 100Mb for data uploading. This is unacceptable for our scenarios!
  • Cloudflare tunnel security method not working with the Immich Android app. But why?

Alt text

Normally, from the client Immich app, the user will enter the backend URL (with the API Key), then the client will be able to upload to the server (if all configuration is correct).

Alt text

However, when enabling Zero Trust and Access Group on the site, the backend URL will be redirected to the Authentication page first. The Immich app basically cannot handle this scenario. A future upgrade may be made on the app but at the moment of writing (25 of Oct), there is no update to resolve this issue.

– Oh, the almighty Abyss. You are such powerful. But your sin is so small, so neglectable. Just tell me, who among the elder gods cares about you and your love for a mortal? And also, how can you be such desperate for one tiny human? That fool did not deserve your attention by the way! He was just a mortal! Maybe less than mortal!

The Abyss smiled lightly, the great being showed a bit of hesitation, but then they slowly replied:

– To Love is to suffer and there can be no love otherwise.

The Abyss paused for a while. The being enjoyed the air and then looked out the window. There was a monstrous chimera outside, the creature patiently waiting for someone. That is such a fascinating behavior because most of the time such a bloodthirsty creature never shows such patience and calmy.

– That chimera is your … pet?

The Abyss stared directly at the man. Then it replied, the voice was cold and unexpectedly rude:

– No! You fool! … Uhm … Do I make you scared? If so then I am so sorry, but let me tell you the other half of the story … You see, I put a part of my soul into my crush … the problem is, I got greedy and more depraved. Living in another body felt so great! But that climax in love slowly unable to satisfy my lust. Soon, I slowly seek for a different feeling, for something … more twisted! A dark art …

– What did you do?

– I put part of my soul into every single living being in the realm. This causes mayhem to the world!

Alternative Solution: Tailscale

What is Tailscale? In short, it is a VPN service that creates a virtual LAN between each client. To connect your device to the Virtual Lan, you must install the Tailscale client on the device (available on Window, Linux, Android, and more). From the Tailscale client, you authenticate to the Virtual Lan (through Google Authentication or something equivalent) then if the authentication is successful, every machine in the same Virtual Lan can ping each other.

Alt text

Each machine was assigned a separate IP address (static IP address) and a machine name, refer to the image below as an example. The image was collected from Google btw.

Alt text

Even greater, tailscale supports MagicDns, which means you can easily connect to the server by using the Machine Name only. For example, in the image above, there is a machine named hello. This means you can access to that device from the browser using the url: http://hello

Sound great? But I don’t prefer this solution, I want to use my custom domain in the form of What I can do to work around it?

– Such lustful magic. So … so that means …

For eternity, the man never imagined someone could do such a depraved act … but still, he listened, very attentive, very respectful.

– Yes … Billions being, billions of emotions, billions of happiness and sadness, all emotions of everyone, I felt them all. At the same time. Needless to say, this was greater than all erotic masquerades combined. Such a mad love. You may think I was so … insane. The elder gods thought the same. At first, they couldn’t understand what had happened. Until one day, you see … the small portion of my soul … it slowly regains … consciousness.

Among every scenario that could have happened, the truth is somehow worse than the worst outcome the man could have thought of.

– And … as you may guessed. The consciousness of each being gained self-consiousness. They also become lustful for sensation! So, as a part of me, they started to do the same process as mine! They started to put another part of their soul in to other beings to enjoy other emotions.

– That’s … that was … impossible. Beyond every imagination of every possible realm! That’s … that can be … So that’s mean three, four, … maybe more soul stays in one body. And that body will enjoy the feelings of so many other bodies. That’s madness.

– As you guessed, at that time, every living being enjoyed the same hedonistic feeling of every other, single, living, being in the world.

– Such a world of madness!

A hybrid solution: Cloudflare as DNS, Tailscale as VPN

To work around this, what you can do is:

  • Setup subdomain on Cloudflare DNS.
  • Route subdomain to the machine’s Tailscale IP address
  • From the server machine, use nginx to route the connection to the correct server porting based on the subdomain’s name.

The diagram below illustrates how the whole system works. Alt text

The sample configuration file for the nginx looks like this.

server {
    listen 80;
        location / {
            proxy_set_header host $host;
            proxy_redirect off;

And now you can visit your server everywhere on Earth by the subdomain:, given that Tailscale VPN has been installed on your devices.

– I have to admit that this depraved feast was so over-indulging … but … things were not so bad right? Nobody died, and one or two may be paralyzed but hedonism doesn’t kill people, … right?

– The thing is … the mortal body … is far weaker than mine. Too much decadence can morph you into … something strange. You see, let’s image one tiny human, let’s feed him with the emotion of a billion other beings in the world. That’s not a hedonistic act, but a fatalistic death! The body of a mortal cannot handle such a maniac amount of pleasure. Worse, for those who are lucky to be alive … there are too many souls and emotions both from the same or different species. Such an insane amount of pressure turns his body into a grotesque creature … a chimera.

The man paused for a moment. He looked outdoors, to the great chimera that was waiting for the owner.

– Is this … your former lover?

– That is what is left of him. Although I cannot consider that creature human anymore.

The air was so heavy and dense that nobody could move. The two figures look at the great chimera for a while. No one can imagine what is going on in their head. The man broke the silence, his curiosity was so strong that he couldn’t resist asking.

– So … what happened next?

Future Study & Conclusion:

Although the resolution using Tailscale & Cloudflare worked quite well at the moment, some concerns to be discussed are the fact that the solution is too reliant on Tailscale VPN. To fix this, I am considering moving the system from Tailscale and self-hosting a similar service on a separate VPN. Headscale is a free alternative and can be self-hosted on a VPS.

This puts the end to this article. At the moment of writing, it was a month ago since I deployed the Immich service successfully. From that point on I put my hand in so many self-hosted services that I felt like I was entering a crazy rabbit-hole =)). One self-hosted app that I highly recommend is Trillium. Spoiler: I used Trilium to draw the diagrams in this article.

Self-hosting was so much fun, and I hope you enjoy the journey as much as I did!

The Abyss replied:

– Not much happened, I gathered all the souls back. For the chimeras, I tried my best to cure their body. I couldn’t remember how many ones I had saved. Some had escaped and as you see … they rampaged the lands. In case you wonder why your kind don’t have any record about this disaster, then it because it happened eons ago, before the Age of Human.

The boys smiled for the first time, before him was not a normal Archimage, but a living being that was so powerful and had lived through so many eras before, yet still, this being was so honest, so truthful, and reliable.

– You are so strong. So insanely strong. I want to be like you, to be a master of Dark Arts.

The Abyss calmly replied:

– I would be glad if you want to be one of my mentees. However, before diving into the world of darkness, here are some words of wisdom for thee!

– And what are they?

The Abyss humed in a very ancient language. But that mysterious language couldn’t be too much challenging for the man. The young scholar realized that what The Abyss humming was a very ancient poem, written in a forgotten language. Here was what the man can understand:

The root of goodness comes from the heart.
The true heart worth more than all the talents combined



  • All images in the blog (aside from the diagrams) were collected from Google.
  • The last poem was from The Tale of Kieu, translated by Vuong Thanh (2020)
  • The first image and the thumbnail were created using Bing AI
Back To Top