Request a Google Photos Takeout export and download every archive; preparation can take minutes to days. Run immich-go upload from-google-photos against your Immich server with the photos and JSON sidecars so it can apply dates, locations and albums. Import and ML indexing are separate jobs with hardware-dependent durations. Back up both the database and original files, then test a restore.
Contents
By LK Wood IV · 2026-06-11 · ~14 min read · St. Louis County, MO
Immich is the only self-hosted photo library that’s actually replaced Google Photos for most users in 2026 — face recognition, CLIP semantic search (“photos of the lake”), the timeline view, and a mobile backup app that runs automatically in the background. This guide sets up Immich from scratch and migrates an existing Google Photos library using the Takeout export path.
For the Immich vs PhotoPrism vs Ente comparison, see the self-hosted photo library showdown.
Hardware requirements
| Scenario | RAM | CPU | GPU |
|---|---|---|---|
| Small library (<20K photos), no active ML | 6GB (Immich’s stated minimum) | 2 cores | Not needed |
| Typical library (20–100K photos) | 8–16GB | 4 cores | Helpful but not required |
| Large library (100K+ photos), fast initial index | 16–32GB | 6–8 cores | CUDA GPU recommended for indexing speed |
| Running alongside other services | Add 4–8GB buffer |
ML indexing and the initial upload have separate queues. Their durations depend on photo/video mix, model, CPU/GPU, storage and concurrent jobs. Measure a representative batch on your server and watch job progress and failures before estimating a large import. GPU support can accelerate eligible ML jobs; it does not set a completion deadline for the whole migration.
Step 1: Deploy Immich with Docker Compose
Create the Immich stack directory:
mkdir -p /opt/stacks/immich && cd /opt/stacks/immich
Download the official Docker Compose files:
wget -O docker-compose.yml \
https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
wget -O .env \
https://github.com/immich-app/immich/releases/latest/download/example.env
Edit .env:
nano .env
Set these values:
# Upload path — where your photos will be stored
# Point this at your NAS mount or a large volume
UPLOAD_LOCATION=/mnt/photos/immich
# Database credentials (generate strong passwords)
DB_PASSWORD=your-strong-db-password-here
# Timezone
TZ=America/Chicago
Create the upload directory:
mkdir -p /mnt/photos/immich
Pin the Immich version — the :release tag in the official compose file is a moving target. Pin to a specific version to avoid surprise schema migrations:
# Find the latest stable release
IMMICH_VERSION=$(curl -s https://api.github.com/repos/immich-app/immich/releases/latest | grep '"tag_name"' | cut -d '"' -f 4)
echo "IMMICH_VERSION=$IMMICH_VERSION" >> .env
Start Immich:
docker compose up -d
Immich starts four containers, immich_server, immich_machine_learning, immich_redis (a Valkey image, despite the name) and immich_postgres (PostgreSQL with VectorChord). The separate immich-microservices container that older guides list was folded into the server (official compose checked 2026-08-22). The initial startup takes 2–3 minutes while the database initializes.
Access the web UI at http://your-host-ip:2283. Create the admin account on first launch.
Step 2: Configure storage and external libraries
In Immich Settings → Storage:
The default storage structure stores originals in UPLOAD_LOCATION/{userID}/{year}/{month}/. If you prefer to preserve the original directory layout from your existing photo archive, configure an External Library instead:
Administration → External Libraries → Create Library:
- Point it at an existing folder (e.g.,
/mnt/photos/archive) that Immich mounts read-only - Immich indexes and searches photos in this location without copying or modifying them
- Use this path for existing photo archives you want to browse in Immich but manage outside of it
For Google Takeout imports and new phone backups, let Immich manage the storage via the Upload path.
Step 3: Mobile backup setup
Immich mobile app is available on iOS and Android (search “Immich” in the App Store / Play Store).
After installing:
- Open the app → enter your server URL:
http://192.168.1.10:2283(or your NPM domain if you’ve set that up) - Log in with your account
- Tap the profile icon → Backup → configure backup on WiFi only or on any network
- Enable “Background Backup” in the app settings
For background backup to work reliably on iOS:
- Go to iOS Settings → Immich → allow background app refresh
- On iOS, the Immich app must be opened periodically to trigger background sync (iOS background task limitation)
For full automatic background backup on Android, enable the “Ignore Battery Optimization” option in the app.
Step 4: Enable hardware ML acceleration
Hardware acceleration dramatically reduces the time to index your library. Configure it based on your hardware.
NVIDIA GPU (CUDA):
In docker-compose.yml, under immich-machine-learning:
immich-machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-cuda
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
The NVIDIA Container Toolkit must be installed on the host:
# On Proxmox or Docker host
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
apt update && apt install -y nvidia-container-toolkit
systemctl restart docker
Intel Quick Sync (VAAPI) / OpenVINO:
immich-machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}-openvino
devices:
- /dev/dri:/dev/dri
After changing the image and adding device access, redeploy:
docker compose pull
docker compose up -d
In the Immich web UI → Administration → Jobs → Machine Learning: confirm the GPU is being used (processing speed should increase noticeably).
Step 5: Migrate Google Photos via Takeout
Request your Takeout export:
- Go to takeout.google.com
- Deselect everything, then select “Google Photos” only
- Choose “Export once”, maximum file size (50GB per archive),
.zipformat - Google emails you when the export is ready. Its Takeout help gives a range of minutes to days, depending on the amount of data.
Download all the zip files to your server. The archive count depends on your library size and the maximum archive size you selected. At up to 50 GB per archive that is a serious one-time download, so if your ISP enforces a monthly cap, check it against your normal usage with the data usage calculator before you kick it off.
Install immich-go, the tool that handles Takeout import correctly (preserving metadata from .json sidecars):
# Get the latest release from GitHub
IMMICH_GO_VERSION=$(curl -s https://api.github.com/repos/simulot/immich-go/releases/latest | grep '"tag_name"' | cut -d '"' -f 4)
# Pick the build for your architecture. Most homelab hosts are x86_64;
# use immich-go_Linux_arm64.tar.gz on a Pi or other ARM box.
ARCH_FILE=immich-go_Linux_x86_64.tar.gz
wget "https://github.com/simulot/immich-go/releases/download/${IMMICH_GO_VERSION}/${ARCH_FILE}"
tar xzf "${ARCH_FILE}"
mv immich-go /usr/local/bin/
Get your Immich API key: Immich web UI → Profile → API Keys → New API Key.
Run the import:
# Extract all Takeout zips to a staging directory
mkdir -p /mnt/staging/takeout
for f in /mnt/staging/takeout-zips/*.zip; do
unzip -q "$f" -d /mnt/staging/takeout
done
# Import with immich-go
immich-go upload from-google-photos \
--server=http://localhost:2283 \
--api-key=YOUR-API-KEY \
/mnt/staging/takeout
# or skip the unzip step and point it straight at the archives:
# immich-go upload from-google-photos --server=http://localhost:2283 --api-key=YOUR-API-KEY /mnt/staging/takeout-zips/*.zip
The from-google-photos subcommand reads the .json sidecar files and applies their metadata; current immich-go releases replaced the old --google-photos and --create-albums flags with it, and album recreation is on by default through --sync-albums (default true in the upload command reference, checked 2026-08-22).
Watch import progress with immich-go output: it reports files processed, skipped and errors. Upload completion does not mean that Immich’s thumbnail, face-detection and search-indexing jobs have finished; check those queues separately.
Handle duplicates: If you’ve already imported some photos via mobile backup before running Takeout, immich-go detects duplicates by file hash and skips them. You won’t get duplicates in your library.
After import completes, let Immich run ML indexing on the new photos. In the web UI → Administration → Jobs → Face Detection: queue this job to run. It can take hours on a large library.
Step 6: Reverse proxy with Nginx Proxy Manager
Get your Immich accessible at a clean URL instead of 192.168.1.10:2283:
In Nginx Proxy Manager → Add Proxy Host:
- Domain:
photos.yourdomain.com - Scheme:
http - Forward to:
immich-servercontainer name (or LAN IP if separate host) - Port:
2283 - SSL: enable with your wildcard cert
- Client max body size: Set in the Advanced tab:
client_max_body_size 0;— required for large file uploads from mobile
Update the mobile app: replace the server URL with https://photos.yourdomain.com.
Step 7: Backup strategy
Immich’s official backup documentation is thorough. The short version:
What to back up:
UPLOAD_LOCATION— all your original photos and videos- PostgreSQL database — ML embeddings, face clusters, album structure, metadata
PostgreSQL backup:
The current manual backup procedure uses pg_dump for the Immich database. Save this script as /usr/local/sbin/immich-db-backup on the Docker host and give it root ownership and mode 0700. Match the container name, database name and username to your Compose configuration. It writes a temporary file in the destination directory, rejects failed or empty dumps, checks gzip integrity, and publishes the final filename only after those checks pass:
#!/bin/bash
set -euo pipefail
umask 077
backup_dir=/mnt/backups/immich
tmp=
trap '[[ -z "$tmp" ]] || rm -f -- "$tmp"' EXIT
trap 'logger -p user.err -t immich-backup "Immich database backup failed"; printf "%s\n" "Immich database backup failed" >&2' ERR
install -d -m 0700 "$backup_dir"
tmp=$(mktemp "$backup_dir/.db-XXXXXX.sql.gz")
docker exec immich_postgres pg_dump --clean --if-exists \
--dbname=immich --username=postgres | gzip > "$tmp"
gzip --test "$tmp"
# Read the whole stream so pipefail also catches decompression errors.
gzip --decompress --stdout "$tmp" | \
awk '{ bytes += length($0) + 1 } END { exit(bytes == 0) }'
final="$backup_dir/db-$(date -u +%Y%m%dT%H%M%SZ).sql.gz"
mv -- "$tmp" "$final"
tmp=
printf 'Completed backup: %s\n' "$final"
Run it once and inspect the completed dump before scheduling it. Failures return a nonzero exit code and reach syslog and stderr. Connect your scheduler’s failure reporting to a monitored destination and test a failure alert; a local log entry alone cannot notify you. After configuring mail delivery, save this as /etc/cron.d/immich-backup with root ownership and mode 0644, replacing the address:
MAILTO=your-monitored-address@example.com
0 3 * * * root /usr/local/sbin/immich-db-backup
Use the saved Compose version and copies of the photo directories for a restore drill on a separate host or isolated Compose project. Follow the matching version’s official restore instructions, start with a fresh database, and use psql --single-transaction --set ON_ERROR_STOP=on for a manual import. Check that accounts and albums return, metadata matches, and sample originals and videos open. Record which dump and Immich version you restored and whether the checks passed. Gzip validity alone does not prove database or file recovery.
Photo backup via restic:
If you’re running restic for off-site backups, add UPLOAD_LOCATION and your database dump directory to the backup paths. Photos are the highest-priority backup target — originals are only in one place.
A common mistake: backing up only the Docker volumes (which contain thumbnails and cache) instead of UPLOAD_LOCATION. Back up the actual photo directory.
Common issues
ML indexing not starting: Check that immich-machine-learning container is running: docker compose ps. If it’s restart-looping, check logs: docker compose logs immich-machine-learning. CUDA issues usually show as “CUDA not available” — verify the NVIDIA Container Toolkit is installed and the container has GPU access.
Google Takeout import skipping files: immich-go skips files it has already imported (by hash). Run with --dry-run first to see what would be imported without committing.
Mobile app can’t connect: Verify the server URL includes the port if you’re using the direct IP (http://192.168.1.10:2283). If using NPM, verify the client_max_body_size 0 setting is in the advanced config and that the SSL cert is valid.
Thumbnails not generating: Jobs in Immich run asynchronously. After a large import, the thumbnail and ML jobs queue behind each other. Check Administration → Jobs — each job type shows queued/active/failed counts. Failed jobs usually indicate a storage permission issue on UPLOAD_LOCATION.
Comparing Immich to PhotoPrism and Ente before committing? The self-hosted photo library showdown covers all three. For access from anywhere, Tailscale or WireGuard give your mobile app a direct path to Immich without exposing the server publicly.
Sources
- Immich Docker Compose install – official docs on the recommended compose deployment, .env config, and container set.
- Immich Backup and Restore – official docs on backing up the upload location and PostgreSQL database.
- immich-go – official repository for the tool that imports Google Photos Takeout and preserves JSON sidecar metadata.
- Installing the NVIDIA Container Toolkit – official guide for the host toolkit required for CUDA machine-learning acceleration.
Frequently asked questions
How long does Google Takeout take to process?
Does Immich import all Google Photos metadata (location, date, albums)?
How much RAM does Immich need?
Can I keep my original photos without Immich modifying them?
What happens to the Google Photos metadata that doesn’t match a file?
Sources and corrections
- Last updated
- Methodology
- See our methodology for research and review standards.
- Corrections
- Spotted an error or a stale number? Email contact@techfuelhq.com. Confirmed corrections are recorded here with the date and change.