← All projects

Project case study / Self-hosted infrastructure

Replacing Google Photos
with a private family photo cloud.

An old ASUS laptop, a photo library worth keeping, and a practical reason to learn infrastructure. Today, four of us host our photos on the same Immich server.

People using it
4 family members
Photo storage
1 TB HDD
Battery charge limit
60%
Off-site backup
AWS Glacier

01 / A useful reason to build a server

I wanted a private alternative to Google Photos for my family: somewhere to upload, browse, and keep our photos on hardware I control. That gave this project a concrete purpose beyond experimenting with another home lab service.

I already had an ASUS VivoBook. Rather than buying a dedicated server, I repurposed it to run Debian and Docker, with Immich providing the photo application. Four members of my family, including me, now host their photos there.

My contribution was the system around Immich: preparing the machine, arranging persistent storage, migrating existing media, configuring remote access, troubleshooting imports, and adapting the laptop for continuous use. Immich itself is the open-source application that makes the photo experience possible.

02 / The system as it exists today

The ASUS has an Intel i3-7020U and 12 GB of RAM. An SSD supports the operating system and Docker, while a 1 TB HDD holds the photo library. Debian provides the host environment; the application runs as Docker services.

01 / Current architecture In use
Four family membersMy phone + three family members’ phones
↓ Photo uploads & library access
Home networkLocal access
TailscalePrivate access away from home

ASUS VivoBook · Debian · Docker

Immich serverPhoto library, uploads & background work
↕ Supporting services
PostgreSQLLibrary metadata
ValkeyRedis-compatible service
Immich MLMachine learning
↓ Immich-managed media
1 TB photo HDDPersistent media storage
Phones reach the ASUS server locally or through Tailscale. The database and media are separate parts of the system; both matter for recovery.

The stack includes the Immich server, PostgreSQL, the machine-learning service, and a Redis-compatible service running Valkey. These have different responsibilities: the media files live on disk, while the database preserves the information that makes those files a usable library.

Tailscale provides the remote access path. I verified access from outside the home using the server’s Tailscale IP and Immich’s port. The HTTPS hostname I tried did not work at that stage, so I am not presenting it as a completed part of the setup.

An earlier Raspberry Pi project used Samba and PhotoSync for photo storage. This case study covers the later ASUS-based Immich system; the Pi is not a dependency in the architecture shown here.

03 / The migration was the real work

Getting the application running was only one part of the project. My existing media was spread across folders and ZIP archives, with duplicate files and large videos to work through. A temporary transfer SSD acted as the import source, separate from permanent storage.

02 / Migration workflow
  1. Existing mediaFolders & ZIP archives on a temporary SSD
  2. Prepare & inspectExtract archives, inspect duplicates, check mounts
  3. Controlled importImmich CLI · dry runs · retries
  4. Immich libraryMedia on the HDD, indexed by Immich
The transfer SSD was an import source, separate from permanent photo storage. Import directories were exposed to the container as read-only mounts.

A file on the host is not automatically visible in a container

At one point, the extracted files were visible on Debian but missing inside Immich’s container. Inspecting the mounts showed the intended mapping, yet the running container had been started before the transfer SSD was mounted. The issue was the container’s view of storage, rather than the extraction itself.

The configuration separated the writable Immich data directory from read-only import sources. That made the boundary explicit: existing media was input; the managed library was the destination.

A busy machine is not always short of CPU

During migration, the server appeared heavily loaded. A closer look at top showed 72.9% I/O wait in one sample, and the process list revealed five simultaneous Immich import or scan processes. Several were reading the same source.

That evidence changed the diagnosis. High apparent activity did not, by itself, justify a processor upgrade. Storage contention and overlapping work were part of the problem. The response was to stop the overlapping imports and use a controlled sequence, rather than launching more retries alongside them.

I also checked a repeated video using SHA-256 hashes. The matching hashes confirmed that those two copies were identical. That is a useful distinction: matching filenames alone do not prove that files contain the same data.

04 / Making a laptop work as a server

A repurposed laptop brings operational questions with it. Photo imports can create bursts of processing, while everyday use is a different workload. Storage, background jobs, heat, and power all deserve attention alongside CPU and memory.

Implemented / Battery care

A 60% charge ceiling

I configured the server to charge only to 60% and remain around that level while plugged in. The intent is to avoid keeping the battery continuously at full charge while the laptop serves as an always-connected machine.

This is a charging-policy change, not a measured battery-lifespan result. I have not quantified the improvement, and the charging limit does not replace attention to temperature or the condition of the hardware.

The goal is a machine I can keep using and maintaining, with operational choices that fit its new role. Owning the server also means owning its updates, capacity, availability, and recovery plan.

05 / Glacier for off-site recovery

Local storage for everyday use. Off-site cold storage for recovery.

The HDD provides the working photo library. AWS Glacier adds an off-site cold-storage layer, so the home server is not the only place the photo archive lives. Immich remains the everyday application; the remote archive serves a different purpose: recovery if the local copy is lost.

03 / Off-site backup & recovery Glacier archive
Running Immich systemPhoto HDD + PostgreSQL database
↓ Create a recoverable backup set
Media filesPhotos, videos & required library files
Database & configurationDatabase dump, deployment details & version record
↓ Off-site cold-storage copy
Amazon S3 Glacier storage classCold archive · separate from the live library
↓ Retrieve backup & rehearse restoration
Recovery rehearsal · next milestoneRestore into a separate environment and verify the library
The local Immich library serves everyday use; Glacier holds the off-site archive. The recovery rehearsal is shown as the next milestone, not a completed test.

A recoverable Immich backup needs both media and a database backup, with deployment configuration and version information to support restoration. Immich’s documentation explains why a database backup alone does not contain the photos and videos. Immich backup and restore guidance ↗

The architecture separates the live library from the cold archive. The Glacier tier does not sit in the upload path, and everyday browsing does not depend on retrieving archived objects. Backup retention, access control, and recovery procedures remain part of operating the system.

Cold storage is for recovery, not the live Immich media directory. Glacier storage classes have different retrieval characteristics; Flexible Retrieval and Deep Archive require a restore before archived objects can be accessed. That makes retrieval time and cost part of the recovery design, rather than something to discover during an outage. AWS storage-class documentation ↗

The recovery path is to retrieve the backup, rebuild the application, and verify that the expected photos, videos, and library information are usable. I am not claiming a measured recovery time or a completed restore drill here: having an off-site archive and proving restoration are separate milestones.

06 / What this project delivers

This project now serves four family members on hardware I already owned. It combines a useful photo application with hands-on work in Linux, containers, persistent storage, private networking, and troubleshooting.

The most valuable lesson was to inspect the whole system. A missing file can be a mount problem. High load can involve I/O wait. A laptop that becomes a server needs a different charging policy. Each of those details matters to the people relying on the result.

The result is a private photo cloud for four people, with a local library for regular use and Glacier as the off-site archive. The next operational milestone is to document and rehearse recovery, so confidence in the backup comes from a repeatable process.

← Explore the other projects