Files
2026-07-12 10:17:17 -04:00

6.5 KiB

Google Takeout → Immich Migration

Companion reference for the immich-server skill. Covers the end-to-end workflow: requesting the export, downloading to the server, and importing with immich-go.

Step 1 — Request from Google

  1. Go to takeout.google.com and sign in
  2. Click Deselect all, then scroll down and check only Google Photos
  3. Optional: click All photo albums included to select specific albums (defaults to all)
  4. Scroll down, click Next step
  5. Configure:
    • Delivery method: Email download link
    • Frequency: Export once
    • File type: .zip or .tgz (no preference difference for immich-go)
    • File size: 2GB, 10GB, or 50GB — larger = fewer parts but longer to generate
  6. Click Create export
  7. Google sends an email to the account when ready (anywhere from 30 minutes to a few days for large libraries)

Step 2 — Transfer to Server

⚠️ Can't download Takeout links from a headless server. Google Takeout download URLs are session-authenticated — they require your logged-in Google browser session. curl/wget from a server hits the sign-in page every time.

Two working approaches:

Option A — Download locally + SCP to server

Download each Takeout part from your phone or computer browser to ~/Downloads/, then transfer over local WiFi:

# From your local machine:
scp ~/Downloads/takeout-*.zip user@192.168.x.x:~/docker/hermes/workspace/google-takeout/

Option B — KasmVNC Chrome container (no local download needed)

Spin up a browser directly on the server and download the files in-place:

# On the server:
docker run -d \
  --name=chrome \
  --shm-size=2g \
  -p 6901:6901 \
  -e VNC_PW=password \
  -e LANG=en_US.UTF-8 \
  -v ~/docker/hermes/workspace/google-takeout:/home/kasm-user/Downloads \
  kasmweb/chrome:1.16.0
  1. Open https://192.168.x.x:6901 in your local browser
  2. Login: kasm_user / password (accept self-signed cert)
  3. Sign into Google inside the containerized Chrome
  4. Open each Takeout link from your email — files save straight to the server
  5. Done? docker rm -f chrome

🐛 Don't use linuxserver/chromium — it uses Selkies WebSocket which often gives a black screen. kasmweb/chrome is the proven workhorse.

🛑 Pitfall: Chrome Safe Browsing kills large Takeout downloads

Large Takeout zips (30-50 GB each) trigger Chrome's FILE_SECURITY_CHECK_FAILED (reason code 40, danger type "UNCOMMON" / type 4). Chrome silently cancels the download, leaving orphan .crdownload files on disk that never finish.

If downloads stall with .crdownload files for >1 hour, follow these steps:

a) Diagnose — check Chrome's History SQLite DB to confirm Safe Browsing is the culprit:

docker cp chrome:/home/kasm-user/.config/google-chrome/Default/History /tmp/chrome_history.db
python3 -c "
import sqlite3
conn = sqlite3.connect('/tmp/chrome_history.db')
cur = conn.cursor()
cur.execute('SELECT id, target_path, state, interrupt_reason FROM downloads ORDER BY id DESC')
for r in cur.fetchall():
    print(f'ID {r[0]}: {r[1] if r[1] else \"(no path)\"} → state {r[2]} reason {r[3]}')
conn.close()
"

b) Fix — disable Safe Browsing via managed policy:

docker exec -u 0 chrome sh -c 'cat > /etc/opt/chrome/policies/managed/download_safety.json << '\''EOF'\''
{
    "DownloadRestrictions": 0,
    "SafeBrowsingEnabled": false,
    "SafeBrowsingProtectionForDownloadEnabled": false
}
EOF
'

c) Restart Chrome so policies take effect:

docker exec -u 0 chrome pkill -f chrome
# KasmVNC auto-restarts Chrome within seconds

d) Clean up orphan files:

rm -f ~/docker/hermes/workspace/google-takeout/*.crdownload
rm -f ~/docker/hermes/workspace/google-takeout/*.tmp

After this, reconnect to KasmVNC, re-open the Takeout download links, and retry. Chrome will no longer block them.

Extract on the server

cd ~/docker/hermes/workspace/google-takeout
mkdir -p extracted

# Install unzip if missing
which unzip || sudo apt install -y unzip

# Extract each zip into the subdirectory (cleaner than extracting in-place)
for f in takeout-*.zip; do
  echo "Extracting $f..."
  unzip -q -o "$f" -d extracted/
done

# For .tgz files:
# for f in *.tgz; do tar -xzf "$f" -C extracted/; done

echo "Done — $(find extracted/ -type f | wc -l) files extracted"
du -sh extracted/

The extracted structure under extracted/ will be a single Takeout/ directory containing:

extracted/Takeout/Google Photos/
  ├── Photos from 2024/
  ├── Photos from 2025/
  ├── <album names>/
  └── ...

Step 3 — Import with immich-go

# Point immich-go at the extracted directory (NOT the zip files):
/usr/local/bin/immich-go \
  --server=http://192.168.x.x:2283 \
  --api-key=YOUR_IMMICH_KEY \
  upload from-google-photos ~/docker/hermes/workspace/google-takeout/extracted/

# The tool:
# - Strips out .json metadata sidecar files automatically
# - Preserves EXIF timestamps and dates
# - Restores album/album structure from Google Takeout format
# - Skips duplicates (matched by content hash)
# - Preserves people tags and archived/trashed state

# If the API key lacks job.create permission, add --pause-immich-jobs=FALSE:
/usr/local/bin/immich-go \
  --server=http://192.168.x.x:2283 \
  --api-key=YOUR_IMMICH_KEY \
  --pause-immich-jobs=FALSE \
  upload from-google-photos ~/docker/hermes/workspace/google-takeout/extracted/

⚠️ Flag gotchas:

  • Use --server and --api-key (double-dash long form). Single-dash -server is parsed as -s + erver= and fails.
  • upload is a parent command — you MUST specify a subcommand: from-google-photos (for Takeout) or from-folder (for raw folders).

Progress updates scroll in terminal. For large imports (38K+ files, 184GB), expect 30-60 minutes depending on server load and disk speed.

Step 4 — Cleanup

After confirming all photos imported successfully:

# Option A — just remove the extracted files, keep zips
rm -rf ~/docker/hermes/workspace/google-takeout/extracted

# Option B — remove everything including zips
rm -rf ~/docker/hermes/workspace/google-takeout

Common Sizes

Google Photos Library Takeout Size Estimate
10 GB used ~10-12 GB
50 GB used ~50-60 GB
200 GB used ~200-240 GB
2 TB used ~2-2.5 TB

Downloading large archives directly to the server (Option B) avoids any need to keep a desktop computer running overnight.