Skip to content

Immich Photo Management

Status: Active Namespace: immich URL: https://photos.skaggsfamily.us Version: v2.4.1

Overview

Immich is a self-hosted photo and video management solution designed as a replacement for Google Photos. It provides automatic backup from mobile devices, AI-powered search, face recognition, and multi-user support with partner sharing capabilities.

Architecture

Core Components

Component Version Purpose Storage Backend
immich-server 2.0+ API server, web UI -
immich-machine-learning 2.0+ Face detection, object recognition Ceph (model cache)
PostgreSQL 16 + pgvecto.rs Database with vector search Ceph block storage
Redis 7.4 Caching and job queue Ephemeral

Infrastructure Dependencies

  • Storage: Hybrid architecture (Ceph for database/cache, NFS for library)
  • Ingress: Traefik IngressRoute with TLS
  • DNS: External-DNS for automatic DNS management
  • Certificate: Let's Encrypt TLS certificate via cert-manager
  • Database: CloudNativePG with pgvecto.rs extension for vector operations

Storage Architecture

Volume Storage Class Size Purpose
immich-library nfs-storage-network 2Ti Photo/video uploads and library
immich-ml-cache ceph-block 10Gi ML model cache
PostgreSQL ceph-block 50Gi Database (managed by CloudNativePG)

Design Principle:

Database and ML cache on Ceph for performance. Photo library on NFS for capacity.

The photo library uses NFS because photos grow indefinitely and require the capacity of network storage. The PostgreSQL database uses Ceph block storage for the fast I/O required by pgvecto.rs vector operations. The ML model cache also uses Ceph for rapid model loading.

Multi-User Support

Immich provides robust multi-user capabilities:

  • Separate Libraries: Each user has their own photo library
  • Partner Sharing: Users can share their entire library with partners
  • Shared Albums: Create albums visible to multiple users
  • External Libraries: Mount NFS paths to import photos without copying

Partner Sharing Setup

After deployment:

  1. Create admin account on first access
  2. Create additional user accounts
  3. Navigate to User Settings > Partner Sharing
  4. Select which users should see your photos
  5. Partners can browse shared photos without duplicating storage

Import Procedures

Bulk Import with immich-go

immich-go is the recommended tool for bulk importing photos into Immich. It's a Go-based CLI that handles Google Takeout's complex JSON metadata matching, preserves albums, and supports resumable imports.

Step 1: Install immich-go on macOS

# Install via Homebrew
brew install simulot/tap/immich-go

# Verify installation
immich-go --version

Step 2: Create User Accounts in Immich

  1. Open https://photos.skaggsfamily.us
  2. First login creates the admin account
  3. Create additional user accounts:
  4. Admin menu → Users → Create User
  5. Create one account per person (e.g., Dan, Wife)

Step 3: Generate API Keys

Each user needs their own API key for imports:

  1. Log in as the user who will own the imported photos
  2. Click user icon (top right) → Account Settings
  3. Navigate to API KeysNew API Key
  4. Name it (e.g., "immich-go import")
  5. Copy the key immediately (it won't be shown again)

API Key Permissions

As of immich-go 0.24+, API keys require these permissions: asset.upload, asset.read, asset.copy, asset.delete, album.create, album.read

Step 4: Organize Your Takeout Files

Locate all Google Takeout ZIP files. They're typically named like:

takeout-20220115T123456Z-001.zip
takeout-20220115T123456Z-002.zip
...

Import All Historical Takeouts

If you have multiple Takeout exports from different years (especially if you deleted videos to free up space), import all of them. immich-go will deduplicate automatically, and you'll recover content that was deleted from Google Photos after export.

Step 5: Run a Dry Run First

Always preview what will be imported before the actual upload:

# Single user import - dry run
immich-go upload from-google-photos \
  --server=https://photos.skaggsfamily.us \
  --api-key=YOUR_API_KEY_HERE \
  --dry-run \
  "/path/to/takeout-*.zip"

# Multiple Takeout folders from different years
immich-go upload from-google-photos \
  --server=https://photos.skaggsfamily.us \
  --api-key=YOUR_API_KEY_HERE \
  --dry-run \
  "/Volumes/External/Takeouts/2022/*.zip" \
  "/Volumes/External/Takeouts/2023/*.zip" \
  "/Volumes/External/Takeouts/2024/*.zip" \
  "/Volumes/External/Takeouts/2025/*.zip"

The dry run shows:

  • Total files found
  • Files that would be uploaded
  • Files that would be skipped (duplicates)
  • Album structure that would be created

Step 6: Run the Import

Once satisfied with the dry run, remove --dry-run to start the actual import:

# Start a tmux session (so import survives terminal disconnect)
tmux new -s immich-import

# Run the import
immich-go upload from-google-photos \
  --server=https://photos.skaggsfamily.us \
  --api-key=YOUR_API_KEY_HERE \
  "/path/to/takeout-*.zip"

# Detach from tmux: Ctrl+B, then D
# Reattach later: tmux attach -t immich-import

Step 7: Import for Additional Users

Repeat for each user with their own API key:

# Wife's import
immich-go upload from-google-photos \
  --server=https://photos.skaggsfamily.us \
  --api-key=WIFE_API_KEY_HERE \
  "/Volumes/External/Wife-Takeouts/**/*.zip"

Resuming Interrupted Imports

If the import is interrupted (network issue, laptop sleep, etc.), simply run the same command again. immich-go will:

  • Skip files already uploaded (hash-based deduplication)
  • Continue with remaining files
  • No manual tracking needed

Useful immich-go Options

Option Description
--dry-run Preview without uploading
--album-from=folder Create albums from folder names
--create-stacks Group RAW+JPEG, bursts, etc.
--when-no-date=FILE Use file modification date if no EXIF
-v Verbose output

Import Progress

Large imports can take many hours. Monitor progress:

# In tmux session, watch the output
# Or check Immich web UI - photos appear as they're uploaded
# Admin → Jobs shows processing queue

iCloud Photos Import

For iCloud photos, export first then use from-folder:

  1. Export from Photos.app on Mac:
  2. Select photos → File → Export → Export Unmodified Originals
  3. Or use iCloud.com → Photos → Download

  4. Import with immich-go:

    immich-go upload from-folder \
      --server=https://photos.skaggsfamily.us \
      --api-key=YOUR_API_KEY \
      --album-from=folder \
      "/path/to/exported/photos"
    

Ongoing Mobile Backup

After importing historical photos, set up the Immich mobile app for automatic backup of new photos:

  1. Install Immich from App Store / Google Play
  2. Server URL: https://photos.skaggsfamily.us
  3. Log in with your credentials
  4. Settings → Backup → Enable Background Backup
  5. Select albums to backup (Camera Roll at minimum)

Dual Backup Strategy

You can keep iCloud Photos enabled alongside Immich. New photos will backup to both services, providing redundancy during the transition period.

Deduplication

Immich uses hash-based deduplication:

  • Duplicate photos are detected during import
  • Only one copy is stored regardless of import source
  • Useful when importing from multiple cloud services with overlapping content
  • Safe to import the same Takeout multiple times

Deployment

Source Files

Location: apps/_bases/immich/

Key files:

  • helmrelease.yaml - Main Immich deployment via Helm
  • postgres-cluster.yaml - CloudNativePG cluster with pgvecto.rs
  • redis.yaml - Redis deployment for caching
  • pvc.yaml - Library and ML cache persistent volume claims
  • ingressroute.yaml - Traefik routing for web UI
  • certificate.yaml - TLS certificate
  • dnsendpoint.yaml - DNS record
  • sealedsecret-*.yaml - Encrypted credentials
  • backup/ - VolSync backup configuration

Helm Chart

  • Repository: https://immich-app.github.io/immich-charts
  • Chart: immich
  • Version: 0.10.3

Dependencies

  • infra-sources-helm - Helm repository
  • infra-namespaces - immich namespace
  • infra-sealed-secrets - Secret decryption
  • infra-traefik - Ingress controller
  • infra-cert-manager - TLS certificates
  • infra-cloudnative-pg - PostgreSQL operator
  • infra-rook-ceph - Ceph storage
  • infra-nfs-csi - NFS storage
  • infra-volsync - Backup operator

Resource Allocation

Component CPU Request Memory Request CPU Limit Memory Limit
Server 250m 512Mi 2000m 2Gi
Machine Learning 500m 2Gi 4000m 8Gi
PostgreSQL 200m 512Mi 2000m 2Gi
Redis 50m 64Mi 200m 256Mi

Total: ~1000m CPU request, ~3Gi memory request

Note: The Machine Learning container has higher resource limits as face recognition and object detection are memory-intensive operations.

Operations

Accessing the Service

# Open web UI
open https://photos.skaggsfamily.us

# Direct pod access (debugging)
kubectl port-forward -n immich svc/immich-server 2283:2283

Monitoring

# Check pod status
kubectl get pods -n immich

# View server logs
kubectl logs -n immich -l app.kubernetes.io/name=immich-server --tail=100

# View ML logs
kubectl logs -n immich -l app.kubernetes.io/name=immich-machine-learning --tail=100

# Check HelmRelease status
kubectl get helmrelease -n immich

# Check PostgreSQL cluster
kubectl get cluster -n immich

Mobile App Setup

  1. Download Immich app (iOS App Store or Google Play)
  2. Enter server URL: https://photos.skaggsfamily.us
  3. Log in with your user credentials
  4. Enable automatic backup in app settings
  5. Configure which albums/folders to backup

Backup and Recovery

Backup Configuration

ML Cache Volume (VolSync):

  • Method: Restic to MinIO S3
  • Schedule: Daily at 4:00 AM
  • Retention: 7 daily, 4 weekly, 6 monthly
  • Destination: s3://volsync-backups/immich-ml-cache

PostgreSQL (CloudNativePG):

  • Method: Barman to Garage S3
  • Continuous WAL archiving
  • Daily full backups
  • Retention: 30 days

Photo Library:

  • Not backed up via VolSync (large volume, stored on NFS)
  • Consider S3 lifecycle policies for offsite backup
  • Photos can be re-imported from mobile devices if needed

Backup Verification

# Check VolSync backup status
kubectl get replicationsource -n immich

# Check PostgreSQL backup status
kubectl get backup -n immich

# View PostgreSQL backup details
kubectl describe cluster immich-postgresql -n immich

Recovery Procedures

ML Cache Recovery:

  1. Create ReplicationDestination pointing to backup
  2. Restore PVC from backup
  3. Models will be redownloaded automatically if backup unavailable

PostgreSQL Recovery:

  1. Stop Immich server deployment
  2. Use CloudNativePG recovery procedures
  3. Restore from backup or point-in-time recovery
  4. Restart Immich server

Troubleshooting

Database Connection Errors

Symptom: Immich pods crash with database connection errors

Diagnosis:

# Check PostgreSQL cluster status
kubectl get cluster -n immich
kubectl describe cluster immich-postgresql -n immich

# Check PostgreSQL pod logs
kubectl logs -n immich -l cnpg.io/cluster=immich-postgresql

Resolution:

  • Verify CloudNativePG operator is running
  • Check secret names match in HelmRelease
  • Verify pgvecto.rs extension is loaded

ML Container High Memory

Symptom: ML container OOMKilled

Diagnosis:

kubectl top pod -n immich -l app.kubernetes.io/name=immich-machine-learning
kubectl describe pod -n immich -l app.kubernetes.io/name=immich-machine-learning

Resolution:

  • Increase memory limits
  • Reduce concurrent ML workers
  • Consider disabling some ML features temporarily

Slow Photo Processing

Symptom: Job queue backing up, slow imports

Diagnosis:

# Check Redis queue status (via Immich admin UI)
# Or check logs for processing times
kubectl logs -n immich -l app.kubernetes.io/name=immich-server | grep -i "processing"

Resolution:

  • Scale up server resources
  • Check NFS storage performance
  • Verify ML container is processing

Authentication

Immich uses native authentication rather than Authentik SSO. This decision was made because:

  • Mobile app compatibility: Immich mobile apps work best with native auth
  • Simpler configuration: No OIDC setup required
  • Robust built-in auth: Immich has comprehensive user management

Authentik integration via OIDC can be added later if desired.

References


Last Updated: January 11, 2026