← Back to Wiki
Self-Hosting

Replace iCloud / NAS Photos With Immich + Radicale

Two focused, best-in-class tools instead of one all-in-one suite: Immich for photos and video, Radicale for contacts and calendars. Here's the setup, and how to migrate years of existing data over without losing anything.

Why two apps instead of one

An all-in-one platform (Nextcloud, for example) can do photos, files, contacts, and calendars all at once — but splitting into two focused tools instead gets you a noticeably better experience for less overhead:

Storage architecture matters for backups

If your backup solution only backs up VM/container disk images (not arbitrary NFS-mounted paths — this is true of Proxmox Backup Server, for example), a simple NFS bind-mount for your photo library means it's never actually covered by your backup job — a real risk for genuinely irreplaceable photos.

Instead, provision a real virtual disk on your NAS-backed storage pool and let your hypervisor manage it as a normal disk image:

# Proxmox example
pct set <vmid> --mp0 nas-storage-pool:500,mp=/mnt/photo-library,backup=1

This gets you a real disk image on the NAS, formatted normally and mounted like local storage inside the container — no NFS UID-mapping headaches, and backup=1 makes it a genuine backup target. Keep the database's own data directory (Postgres, in Immich's case) on the container's local storage, never on network storage — database files over NFS risk locking issues and corruption.

Gotcha: don't move an app's storage path after it's already initialized without also resetting its state. Some apps (Immich included) write internal marker files into their storage location on first boot and check for them on every subsequent start. Swapping to a fresh disk after the fact can leave the app crash-looping, checking for markers that only exist in the old location. If no real user data has been imported yet, the simplest fix is a full reset (wipe both the database and the new storage location, then let the app initialize both fresh together). For an instance with real data already in it, follow the app's own documented storage-migration process instead.

Migrating photos off a vendor NAS app

The same setuid-rsync-over-SSH restriction covered in the password manager guide applies here too — use a tar stream over SSH instead:

ssh <nas> 'cd /path/to/photos && sudo tar --exclude=@eaDir --exclude=.DS_Store -cf - .' \
  | ssh <immich-host> 'cd /import/staging && tar xf -'

Exclude any vendor-internal thumbnail-cache directories (Synology's @eaDir, for example) — they're pure noise your new tool will regenerate its own thumbnails for anyway.

To actually ingest the files (not just copy them), use immich-go rather than a raw file copy — it reads EXIF/embedded dates and uploads through Immich's real API so thumbnails and search indexing run normally:

immich-go upload from-folder \
  --server http://localhost:2283 --api-key <your-api-key> \
  --folder-as-album FOLDER --no-ui \
  /import/staging

Always run with --dry-run first at any real scale — it's cheap insurance and can catch permission issues (e.g. a non-admin API key can't pause background jobs, a step immich-go attempts by default) before touching anything for real.

Don't panic over asset-count changes after cleanup. If your video count suddenly drops after deleting auto-created albums, check whether it correlates with Live Photo linking (iPhone photo/video pairs getting merged into a single timeline entry) before assuming data loss — it's the same behavior Google/Apple Photos use for the same feature.

Migrating contacts and calendars

Most vendor NAS calendar/contact apps expose standard CalDAV/CardDAV endpoints directly, even without an obvious "export" button in the UI — check the app's underlying web server config for existing /caldav/ and /carddav/ routes. If they exist, use vdirsyncer, a standard two-way CalDAV/CardDAV sync tool, to do a one-time pull from the old source into Radicale.

Gotcha: some NAS vendors issue calendar/contact certificates from their own "Managed CA," not a self-signed cert. If vdirsyncer's SSL verification option only accepts a real CA file (not a boolean to disable verification), and the standard public root for that vendor's cert type doesn't match, pin the exact certificate's fingerprint instead of trying to chase down the right CA chain:
echo | openssl s_client -connect <nas-ip>:5001 -servername <nas-ip> 2>/dev/null \
  | openssl x509 -noout -fingerprint -sha256

Verify the migration by comparing item counts on both sides directly — not just a "sync exited cleanly" check — before considering it done.

Connecting devices

The actual cutover is per-device: repoint each phone and computer's CalDAV/CardDAV account from the old server to Radicale. Most desktop OSes have this under their native "Internet Accounts" / account settings as a generic CalDAV/CardDAV option; Android has no built-in client and needs a third-party app (e.g. DAVx⁵).