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:
- Create admin account on first access
- Create additional user accounts
- Navigate to User Settings > Partner Sharing
- Select which users should see your photos
- 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¶
Step 2: Create User Accounts in Immich¶
- Open https://photos.skaggsfamily.us
- First login creates the admin account
- Create additional user accounts:
- Admin menu → Users → Create User
- Create one account per person (e.g., Dan, Wife)
Step 3: Generate API Keys¶
Each user needs their own API key for imports:
- Log in as the user who will own the imported photos
- Click user icon (top right) → Account Settings
- Navigate to API Keys → New API Key
- Name it (e.g., "immich-go import")
- 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:
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:
- Export from Photos.app on Mac:
- Select photos → File → Export → Export Unmodified Originals
-
Or use iCloud.com → Photos → Download
-
Import with immich-go:
Ongoing Mobile Backup¶
After importing historical photos, set up the Immich mobile app for automatic backup of new photos:
- Install Immich from App Store / Google Play
- Server URL:
https://photos.skaggsfamily.us - Log in with your credentials
- Settings → Backup → Enable Background Backup
- 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 Helmpostgres-cluster.yaml- CloudNativePG cluster with pgvecto.rsredis.yaml- Redis deployment for cachingpvc.yaml- Library and ML cache persistent volume claimsingressroute.yaml- Traefik routing for web UIcertificate.yaml- TLS certificatednsendpoint.yaml- DNS recordsealedsecret-*.yaml- Encrypted credentialsbackup/- VolSync backup configuration
Helm Chart¶
- Repository: https://immich-app.github.io/immich-charts
- Chart:
immich - Version: 0.10.3
Dependencies¶
infra-sources-helm- Helm repositoryinfra-namespaces- immich namespaceinfra-sealed-secrets- Secret decryptioninfra-traefik- Ingress controllerinfra-cert-manager- TLS certificatesinfra-cloudnative-pg- PostgreSQL operatorinfra-rook-ceph- Ceph storageinfra-nfs-csi- NFS storageinfra-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¶
- Download Immich app (iOS App Store or Google Play)
- Enter server URL:
https://photos.skaggsfamily.us - Log in with your user credentials
- Enable automatic backup in app settings
- 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:
- Create ReplicationDestination pointing to backup
- Restore PVC from backup
- Models will be redownloaded automatically if backup unavailable
PostgreSQL Recovery:
- Stop Immich server deployment
- Use CloudNativePG recovery procedures
- Restore from backup or point-in-time recovery
- 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¶
- GitOps Repository: flux-repo/apps/_bases/immich
- Helm Chart: immich-app/immich-charts
- Official Docs: Immich Documentation
- Mobile Apps: iOS / Android
Last Updated: January 11, 2026