One Year of Photo Management with Raspberry Pi
Table of Contents
Published Date:
Last Updated:
Introduction
It has been operating stably for about a year since its launch, so I’d like to review the construction process and any challenges I encountered.
Motivation
My first child was born in the first half of 2024, and the number of photos and videos rapidly increased. Until then, I had been using Google Photos’ free plan, but with the end of unlimited storage, it was limited to about 16GB, which was definitely reaching its limit.
In this article, “photos” refers to “photos and videos.”
Every time I take a picture of my son, I worry about storage space… I want to take long videos without worrying… The storage problem was an issue that needed to be resolved.
The achievement conditions for this challenge are the following five:
- Effectively unlimited storage for photos and videos.
- Ability to save and view from a smartphone.
- No subscriptions.
- Ability to transfer a large amount of content saved in Google Photos.
- Easy sharing of my son’s photos with my parents who live far away.
- I want to avoid the hassle of sending them via LINE every time, and instead have a state where they can just “tap here in this app to see their grandchild’s photos!”
“No subscriptions” is because I would have to pay continuously unless I delete photos. My current (August 2025) photo and video data totals 265.6GB.
If I were to manage this amount with a subscription…
- iCloud 2TB ¥1,500/month
- Google 2TB ¥1,450/month
- Amazon Photos rejected because videos are limited to 5GB.
- Mitene rejected because video length is limited to 2 minutes.
Even with Google, it costs ¥17,400 per year, and living with the thought of that continuing indefinitely is stressful. Personally, I feel it’s better for my mental health to buy an HDD and manage it myself.
In this article, I will explain:
- Building an unlimited photo storage (1.2.3.4.)
- Sharing photos with family in remote locations (5.)
Final Diagram

The diagram above shows the flow of the Immich server within the LAN being exposed externally via Cloudflare. Mobile apps access it via tokens, and web apps access it via SSO authentication.
-
Characters
- Immich: Open-source photo and video management application
- Raspberry Pi: Server hosting Immich
- HDD/Case: Storage for DB and photos
- Cloudflare: Domain for external communication, tunneling
Goal 1: Building an Unlimited Photo Storage
To enable photo storage and viewing within the home, I will build the following environment:
What You Need
You need a photo management application, a server to host it, and storage space.
Application
There are many open-source photo applications, but since my family only takes photos with smartphones, the existence of a mobile app is crucial.
Looking at the comparison table below, I adopted Immich which has a mobile app and album features.
Server
I chose Raspberry Pi 4 as the server.
Although it’s not the main purpose, I wanted to put it in a clear case and have a rugged look with the board visible. I used to like the skeleton models of Game Boy and NINTENDO64…
Raspberry Pi is small and easy to handle, and clear cases are readily available, making it an easy choice.

Note that Immich must meet the essential requirements.

Storage Area
For capacity, I prepared two physical HDDs (one for backup).
Now that I have all the necessary items, let’s set up the Raspberry Pi.
Raspberry Pi and HDD Setup
OS Installation
Basically, follow the official procedure. The process is to insert the SD card with the OS into the Raspberry Pi and boot it.

Local IP Address Fixation
Once the Raspberry Pi starts up, fix the local IP address. This IP will be used to log in to the Raspberry Pi and access Immich from home. Since automatic assignment by DHCP can cause the IP to change, it’s better to fix it.
In the router’s DHCP settings screen, manually set the MAC address of the Raspberry Pi’s wired LAN and an arbitrary IP address.
- Open the command line on the Raspberry Pi.
- Type
ip a
.ip a
- It’s written after
eth0:
’slink/ether
.
If you use Wi-Fi, you should look for wlan0
, but wired is recommended for speed.
Note that if the desired IP address is already set on another device, it cannot be manually assigned as is. If you absolutely want to use a specific IP address (11.14 in this example), follow these steps:
- Disconnect the device currently using the specific IP address from the network.
- Restart the router to reset the IP address assignment.
- Manually set it.
Home router DHCP servers often assign “from available numbers in order,” so younger numbers tend to be filled first (this varies depending on the model and settings). Therefore, if you want to use a younger number, you will likely need to disconnect many devices and restart the router.
Also, searching for “Raspberry Pi fixed IP” will show various methods, such as editing /etc/dhcpcd.conf
or configuring NetworkManager
. However, none of them worked for my environment… Finally, I succeeded by setting it on the router side. By the way, the official Raspberry Pi also recommends this method.
-
Very helpful article:
ラズベリーパイのIPアドレス固定は、ラズパイ側で設定 "しない" のが推奨らしい - Qiita要約 Raspberry Piの公式ドキュメントによると、ラズパイ側でIPアドレスの固定を設定するのではなく、ルータ側のDHCPの設定でMACアドレスと対応させて静的IPアドレスを設定するのが推奨らしい。 To allocate a...qiita.com
Remote Control via VNC or SSH
Once the Raspberry Pi is set up, it’s convenient to be able to operate it remotely, rather than connecting a monitor every time.

Hard Disk Formatting and Partitioning
Next, install the HDDs in the HDD case and connect the Raspberry Pi to the HDD case.
/mnt/hdd1
and the backup drive to /mnt/hdd2
.
So far, I have installed the OS on the Raspberry Pi, fixed the local IP address, and mounted the HDDs. Next is the introduction of Immich.
Immich Setup
Docker Setup and Configuration File Preparation
It’s easiest to set it up with Docker, just like the official quick start.
.env
file, the photo upload destination and DB save destination will be hdd1
, which was mounted earlier.
UPLOAD_LOCATION=/mnt/hdd1/immich/images
DB_DATA_LOCATION=/mnt/hdd1/immich/postgres
Immich Admin Account Creation
Once the containers are successfully launched with docker compose up -d
, you can access Immich at http://<machine-ip-address>:2283
. After accessing, create an admin user.
StorageTemplate
I have set it up so that files are saved in a format like UPLOAD_LOCATION/library/username/2022/2022-02-03/IMAGE_56437.jpg
.
This setting will be applied to photos added after the change. For items already added before the change, you can apply the setting by clicking the Jobs → Migrate Storage Template
button.
So far, the setup of Raspberry Pi, HDD, and Immich is complete. Next, I will save photos and videos from Google Photos to the HDD via Immich.
Photo Migration from Google Photos
I will use immich-go
. This is a command-line tool that can migrate photos from Google Photos to the storage area managed by Immich.
The necessary materials are:
- immich-go
- Google Takeout
- Exported data saved in your Google account
- Immich API key for each user account
- Key required to operate Immich from outside the application
Specifically, by executing the command:
./immich-go -server=http://<machine-ip-address>:2283 -key=<Immich API key created> upload -create-albums -google-photos <path to downloaded Google Photo Takeout>
you can import content downloaded from Google into an arbitrary user’s content in Immich.
About Photo Duplication
Before backing up, you need to organize “where your photos are.” If all your past photos are on your smartphone, you just need to launch the Immich mobile app and back up from your smartphone. However, in reality, “photos only on Google Photos,” “photos only on your smartphone,” and “photos on both” are mixed.
// Image of photos saved on Google Photos and photos saved on smartphone partially overlapping
Timeline: 2008 ------2017-------2023----------> New
Google Photos: [===========++++++++++]
Smartphone : [++++++++++==========]
It’s a waste of space if the same photos are backed up. In this policy, I decided to migrate photos from Google Photos from 2008 to 2023, and then back up directly from my smartphone thereafter.
It’s difficult to strictly separate them, and duplication will occur around 2024, but Immich has a duplicate detection and deletion function, so some duplication is acceptable.
immich-go
<> Code
> Download ZIP
.
Google Takeout
You can export Takeout from here:
Next step
.
Create export
.
In my case, I downloaded for two accounts: mine and my wife’s.
Immich API Key
Next, issue an API key
. This key is necessary to operate Immich user accounts from immich-go
. It is issued for each user account.
Steps:
- Log in to Immich.
- Account settings.
- API keys.
- New API key.
- Enter an arbitrary name and click Create.
- A new key will be issued, so copy it.
I created two, one for my account and one for my wife’s account.
Migration with immich-go
Before executing the command, unzip the Google Takeout
zip file and check its contents. This time, since the policy is to back up photos from 2024 onwards from the smartphone, zip files containing data from that period are not needed.
Example: If Takeout is split into 14 parts, and zip files from 11/14 onwards contain 2024 data, we will not use 11-14, so move them elsewhere.
Once the necessary zip files are ready, navigate to the directory where immich-go
was downloaded and execute the following command:
./immich-go -server=http://<machine-ip-address>:2283 -key=<Immich API key created> upload -create-albums -google-photos /takeout_directory_name/takeout-*.zip
You can also use the --dry-run
option to preview the operations that will be performed.
After executing the command, wait a while, and images and videos should be imported into Immich. My account had many photos, so I executed the command and went to sleep 🛌. I remember it was finished by the next morning.
I executed it for both my account and my wife’s account.
Mobile App Settings
Next, let’s download and set up the Immich mobile app.
Downloading, logging in, and backing up can all be done by following the documentation below:
http://<machine-ip-address>:2283
.

Backing Up HDD1
Finally, I want to save the data from HDD1 to HDD2 as well. If HDD1 fails, all assets will be lost.
For backing up from HDD1 to HDD2, I use Borg
. It’s a differential backup tool.
borg
, so I adopted it.
immich-borg.sh
and set up a crontab to run it every Saturday at 1:00 AM.
0 1 * * 6 /immich-borg.sh
Done
With this, I have set up an environment where I can log in to Immich from my smartphone and back up photos and videos directly to the HDD. All the initial conditions have also been cleared.
All initial conditions are cleared:
- 1. Effectively unlimited storage for photos and videos.
- Because I can change to a larger HDD.
- 2. Ability to save and view from a smartphone.
- 3. No subscriptions.
- 4. Ability to transfer a large amount of content saved in Google Photos.
By backing up to another HDD once a week, data redundancy is also ensured1.
In addition, with Immich’s album feature, I can create “Son’s Album” or “Travel Album” and both my wife and I can add photos. The sharing style we used to do with Google Photos and LINE has also been realized as is.
With this,
- Goal 1: Building an Unlimited Photo Storage
is cleared.
Goal 2: Securely Sharing Photos with Remote Family
Now, the photo management between my wife and I has been resolved. However, the next challenge is sharing with parents. Until now, my wife and I had been sending photos to our respective parents via LINE. This was quite troublesome 😇. I really wanted a mechanism where they could view photos whenever they wanted, all in one place.
Fortunately, Immich allows multiple accounts to be created. It also has an album feature, and permissions can be managed per album. Therefore, I can create an account for my parents and grant them viewing access to specific albums. My parents can then open the app and tap on an album to view cute photos of their grandchild 👶.
To solve this problem, I will use Cloudflare Tunnel
.
Cloudflare Tunnel × Cloudflare Access
Cloudflare Tunnel is a mechanism that allows external exposure without a public IP or port forwarding, simply by installing a lightweight tool (cloudflared
) on the server. All communication goes through Cloudflare, so it can be handled securely.
Also, combining it with Cloudflare Access
authentication makes limited public access easy.
Now, let’s set it up.
Final Diagram (Cloudflare)
Obtaining a Domain with Cloudflare Registry
Sign up for Cloudflare and navigate from the dashboard to Domain Registration > Register Domain
to acquire an arbitrary domain.
Creating a Tunnel
Once the domain purchase is complete, create a tunnel.
Steps:
Dashboard
Side Menu
Zero Trust
Network
Tunnels
Create a tunnel

Select Cloudflared
Save with an arbitrary name
Next
Debian
and execute the displayed command on the server.
- Subdomain: Arbitrary name
- Domain: Select from the dropdown list of domains purchased from Cloudflare.
- Service: HTTP
- URL: localhost:2283
Adding Authentication with Cloudflare Access
However, leaving it as is is dangerous without access restrictions. I will set it up so that only specific individuals can access it.
Authentication from Web Browser
First, set up Email authentication when accessing Immich from outside the LAN via a browser.
Navigate to the policy addition screen:
Access
Policies
Add a policy
- Set an arbitrary name for the policy.
- Select Emails in the selector.
- Add email addresses that will be subject to SSO authentication.
- Save.
Access
Applications
Add an application
Self-hosted
Select
- Add an arbitrary application name.
- Click Add Public Hostname > Input field for Public Hostname appears.
- Enter the same subdomain and domain as for the Tunnel.
- In the Access Policy field, click
Select existing policy
.

- Check the policy created earlier and confirm.
- Proceed with
Next
until the Save button appears, then save.
Authentication from Mobile App
Next, how to access from the Immich mobile app. Since the mobile app cannot transition to an authentication screen like a browser, you cannot log in as is. Therefore, I will utilize Cloudflare’s Service Token
and the Immich mobile app’s Custom Proxy Header Settings
.
A Service Token
is a key for apps and programs to access restricted resources on Cloudflare.
The Immich mobile app has a Custom Proxy Header
setting that allows you to include arbitrary values in requests when accessing the server. By setting the token here, you can bypass authentication.
Cloudflare Settings
Now, let’s create a Service Token.
Cloudflare Zero Trust Home
Access
Service Auth
Create a Service Token

- Enter an arbitrary token name.
- Enter an arbitrary expiration date.
- Generate token.
Client ID
and Client Secret
will be displayed as follows, so copy them.
Next, create a policy again. Select Service Token from the selector and choose the token created earlier from the dropdown.
Access
Applications
- Three dots of the created application name.
Edit
Tag: Policy
Select existing policy
- Select the created Service Token policy.
Confirm
Save application
Immich Mobile App Settings
Next, in the Immich mobile app, set it to include the token when accessing.
- Open the Immich mobile app login screen.
- Tap the gear icon.
- Tap Custom Proxy Headers.
- Enter the following in the proxy headers:
- Header and value of the generated Client ID.
- Header and value of the Client Secret.
- Create accounts for both parents in Immich.
- Grant viewing permissions to the son’s album for both parents’ accounts.
- Download the mobile app to both parents’ smartphones.
- Set up custom proxy headers in the mobile app.
- Log in with each account.
Now, my parents can log in with their own accounts and tap on an album to view photos of their grandchild anytime.
Done👍️
With this,
- Securely sharing photos with remote family
is cleared.
Conclusion
With this setup, I was able to meet all the following conditions:
- Effectively unlimited storage for photos and videos.
- Ability to save and view from a smartphone.
- No subscriptions.
- Ability to transfer a large amount of content saved in Google Photos.
- Easy sharing of my son’s photos with my parents who live far away.
Let’s review the costs and pros/cons.
Costs
Item | Unit Price | Quantity | Subtotal |
---|---|---|---|
Raspberry Pi 4 (8GB) | ¥12,000 | 1 | ¥12,000 |
HDD 1TB | ¥7,000 | 2 | ¥14,000 |
HDD Case (Stand) | ¥4,000 | 1 | ¥4,000 |
Miscellaneous (SD, Cable) | ¥600 | 1 | ¥600 |
Total | ¥30,600 |
Item | Unit Price | Quantity | Subtotal |
---|---|---|---|
Domain | ¥1,000 | 1 | ¥1,000 |
Annual Total | ¥1,000 |
Google One (2TB) is ¥17,400/year. Although there are initial costs, it’s a good deal if operated for less than 2 years…?!
Pros and Cons
Pros:
- No subscription fees (capacity is virtually unlimited if HDDs are added).
- Good for learning as an engineer.
Cons:
- Initial setup effort (requires understanding Docker, Cloudflare, etc.).
- High risk if HDD breaks. Difficult to diversify risk compared to cloud.
- Immich updates sometimes have breaking changes, requiring careful research and handling.
Hmm… not many pros…? 🧐
It’s great for engineers to learn and as a hobby, but if you just want to save a lot of photos and videos, subscribing is faster.
Future Challenges
- I want to set up another backup location.
- HDD2 is constantly connected and spinning, despite being for weekly backups.
- Can it be set to only run when needed and sleep otherwise?
- Immich freezes for about a minute when opened on iPhone.
- This is likely because there are too many photos locally on the phone, and it takes time to check if they have been backed up.
- I want to investigate this if I have time.
-
According to the 3-2-1 backup strategy, another backup should be prepared in a different location. ↩︎
Comments
comments powered by Disqus