homelab-blueprint/docs/immich/mobile-sync.md

8.9 KiB
Raw Blame History

Immich Mobile Sync — Phone → NAS → External Library

This is the workaround for the known Immich issue where deleting a photo from the Immich server causes the mobile app to re-upload it on the next sync. The native Immich mobile backup uses hash-based deduplication: if the server hash is gone, it treats the file as new.

This setup avoids that entirely. The phone syncs to a NAS share using PhotoSync/FolderSync, which tracks what it has already sent in its own internal database — not by checking whether the file still exists on the destination. So deleting from Immich does not trigger a re-upload.

Phone  ──(SMB)──▶  UNAS Pro (mobile_photos share)
                         │
                   (NFS → Proxmox host → LXC bind mount)
                         │
                   Immich external library scan

Part 1 — Create the UNAS Pro Share

1a. Create the share

  1. Open UniFi DriveSharesCreate Share
  2. Name: mobile_photos
  3. Leave the quota unset (or set as preferred)
  4. Save

1b. Enable SMB (for phone access)

  1. Click the mobile_photos share → Sharing Protocols
  2. Enable SMB/CIFS
  3. Under SMB settings, set access to Read/Write
  4. Save

1c. Enable NFS (for Proxmox)

  1. Still in the mobile_photos share settings → Sharing Protocols
  2. Enable NFS
  3. Under NFS Hosts, add the Proxmox host IP: 10.0.10.10
  4. Set squash mode to No Root Squash — required so chown works from the Proxmox host (same reason as immich_data)
  5. Set access to Read/Write
  6. Save

Part 2 — Mount on the Proxmox Host

Run these on the Proxmox host as root.

2a. Confirm the SMB share is available

mobile_photos is mounted on Proxmox over SMB/CIFS, using the shared host credentials file at /etc/unas-smb-credentials.

2b. Create the mountpoint

mkdir -p /mnt/unas/mobile_photos

2c. Add the fstab entry

Open /etc/fstab and add this line alongside the existing UNAS entries:

//10.0.10.25/mobile_photos  /mnt/unas/mobile_photos  cifs  credentials=/etc/unas-smb-credentials,vers=3.0,iocharset=utf8,ro,nofail,_netdev,x-systemd.automount,x-systemd.mount-timeout=10s  0  0

2d. Apply and verify

systemctl daemon-reload
umount -lf /mnt/unas/mobile_photos
mount -a
findmnt /mnt/unas/mobile_photos
ls -la /mnt/unas/mobile_photos | head -20

2e. Set ownership

No chown is needed here. The host mount is read-only, and Immich only reads from it.


Part 3 — Wire into LXC 102

3a. Add the bind mount to the LXC

The LXC must be stopped first to add a new mountpoint:

pct stop 102
pct set 102 -mp1 /mnt/unas/mobile_photos,mp=/mnt/unas/mobile_photos
pct start 102

mp1 — use the next available index. Run pct config 102 | grep mp to confirm what indices are already in use.

Verify it's visible inside the LXC:

pct exec 102 -- ls /mnt/unas/mobile_photos

3b. Add the volume to the Immich docker-compose

Add a new volume entry to the immich-server service in /opt/immich/docker-compose.yml:

pct exec 102 -- python3 /dev/stdin <<'PYEOF'
with open('/opt/immich/docker-compose.yml', 'r') as f:
    content = f.read()
old = '      - /mnt/unas/immich_data/upload/upload:/mnt/external:ro'
new = '      - /mnt/unas/immich_data/upload/upload:/mnt/external:ro\n      - /mnt/unas/mobile_photos:/mnt/mobile:ro'
content = content.replace(old, new, 1)
with open('/opt/immich/docker-compose.yml', 'w') as f:
    f.write(content)
print('done')
PYEOF

Verify:

pct exec 102 -- grep 'mobile' /opt/immich/docker-compose.yml

3c. Recreate the immich-server container

pct exec 102 -- bash -c 'cd /opt/immich && docker compose up -d --no-deps --force-recreate immich-server'

Confirm the mount is visible inside the container:

pct exec 102 -- docker exec immich_server ls /mnt/mobile

Should return an empty directory (until photos are synced from the phone).


Part 4 — Add /mnt/mobile as an Immich Import Path

The existing "NAS Photos" external library currently imports from /mnt/external/93f85c12-fc70-465b-a05d-83c55a8e6ccc (the old photo archive). Add /mnt/mobile as a second import path so new phone photos appear in the same library.

# Get a fresh token
TOKEN=$(curl -s -X POST http://10.0.10.15:2283/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"brian.method@gmail.com","password":"<your-admin-password>"}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["accessToken"])')

# Update the library import paths
curl -s -X PUT http://10.0.10.15:2283/api/libraries/8c59305e-5c1d-4957-8354-75ccb8085961 \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "importPaths": [
      "/mnt/external/93f85c12-fc70-465b-a05d-83c55a8e6ccc",
      "/mnt/mobile"
    ]
  }' | python3 -c 'import sys,json; d=json.load(sys.stdin); print("importPaths:", d["importPaths"])'

To trigger an immediate scan after the first phone sync:

curl -s -X POST http://10.0.10.15:2283/api/libraries/8c59305e-5c1d-4957-8354-75ccb8085961/scan \
  -H "Authorization: Bearer $TOKEN"

Immich also runs scheduled library scans automatically — check Administration → Jobs → Library to see the interval.


Part 5 — Set Up the Phone App

Android — FolderSync Pro

FolderSync Pro is the recommended Android app. The free tier supports one folder pair; the paid version removes that limit.

  1. Install FolderSync Pro from the Play Store
  2. Go to AccountsAdd accountSamba (SMB/CIFS)
  3. Fill in:
    • Server: 10.0.10.25
    • Share: mobile_photos
    • Username/Password: your UNAS user credentials
  4. Test the connection, then save
  5. Go to Folder pairsAdd folder pair
  6. Configure:
    • Left folder: your phone's camera folder (e.g. DCIM/Camera)
    • Right folder: the mobile_photos SMB share root (or a subfolder like phone/<device-name>)
    • Sync type: To right folder (one-way upload only)
  7. Under Advanced settings:
    • Delete destination files if source is deleted: OFF — keeps photos on NAS even if you delete from phone
    • Use temp file for upload: ON (safer)
    • Sync hidden files: OFF
  8. Under Scheduling: set to sync every 1560 minutes, or on Wi-Fi connect
  9. Save and run a manual sync to test

Why this avoids re-uploads: FolderSync records every successfully transferred file in an internal SQLite database keyed by file path and modification time — not by checking the destination. If you delete a photo from the NAS (via Immich), FolderSync's internal record still shows it as "already synced" and skips it on the next run.

iOS — PhotoSync

  1. Install PhotoSync from the App Store (one-time purchase for full features)
  2. Open the app → Settings (gear icon) → Configure Targets+
  3. Choose NAS / Network DriveSMB
  4. Fill in:
    • Server: 10.0.10.25
    • Share: mobile_photos
    • Username/Password: your UNAS credentials
  5. Test and save
    • If the SMB connection fails from the FAMILY VLAN, add the UniFi policy Allow FAMILY to UNAS for TCP/445.
  6. Back on the main screen, tap Autotransfer → enable it
  7. Under Autotransfer settings:
    • Transfer: New photos and videos only — this is the critical setting
    • Transfer to: the SMB target you just configured
    • Subfolder: phone/<device-name> (optional, useful if multiple phones share the same share)
    • Delete after transfer: OFF
    • Transfer on: Wi-Fi only (recommended)
  8. Trigger a manual transfer first to confirm connectivity

Why "New photos and videos only": PhotoSync tracks every transferred file in its own database. Deleting from the NAS or Immich does not cause a re-transfer — PhotoSync only looks at what it hasn't sent yet, not what is currently on the destination.


Part 6 — Update the Template and Docs

Update docker-compose-files/immich/template.yaml and docker-compose-files/immich/README.md to document the second volume mount ({{IMMICH_MOBILE_SYNC_DIR}}:/mnt/mobile:ro) and the IMMICH_MOBILE_SYNC_DIR env var alongside IMMICH_EXTERNAL_LIBRARY.

Also add mobile_photos to the share map in docs/unas/nfs-mounts.md as the SMB/CIFS exception.


Verification Checklist

  • findmnt /mnt/unas/mobile_photos shows a cifs mount on the Proxmox host
  • pct exec 102 -- docker exec immich_server ls /mnt/mobile succeeds
  • Immich library shows /mnt/mobile in its import paths
  • Phone sync app completes a test run with no errors
  • After the first phone sync, trigger a library scan and confirm new photos appear in Immich
  • Delete one test photo from Immich, wait for next phone sync, confirm it is not re-imported