--- name: immich-import description: Import photos into Immich from Google Takeout, local folders, and other sources using immich-go CLI tags: - immich - google-takeout - photo-import - self-hosting - immich-go --- # Immich Photo Import Import photos into a self-hosted Immich server. Covers Google Takeout imports (the most common case), plain folder uploads, and troubleshooting. ## When to load this skill - User says "import photos to Immich", "Google Takeout", "immich-go", or "upload to Immich" - User is migrating from Google Photos to Immich - User has a folder of photos to bulk-upload to Immich ## Prerequisites - Immich server URL (e.g. `http://192.168.50.150:2283`) - API key with sufficient permissions (needs at minimum `user.read` + upload rights) - `immich-go` binary installed (for large imports) or the Immich CLI inside the server container ## Workflow ### 1. Verify immich-go is installed ```bash /usr/local/bin/immich-go version ``` Install if needed (from [simulot/immich-go](https://github.com/simulot/immich-go) releases). ### 2. Get a valid API key **Option A — Via Immich web UI:** - Settings → API Keys → Create New Key → name it → copy the key **Option B — Via Immich API (if you know the password):** ```bash # Login and create key TOKEN=$(curl -s -X POST http://:2283/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"admin@example.com","password":"..."}' | python3 -c "import json,sys; print(json.load(sys.stdin).get('accessToken',''))") curl -s -X POST http://:2283/api/api-key \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{"name":"immich-go-import"}' ``` **Option C — Find admin email from DB:** ```bash docker exec immich_postgres psql -U postgres -d immich -c \ 'SELECT id, email, name, "isAdmin" FROM "user";' ``` ### 3. Google Takeout Import #### 3a. Download the Takeout zips If using Chrome in a KasmVNC container to download and Chrome kills the downloads: **Fix Chrome Safe Browsing (kills large Takeout zips):** ```bash docker exec -u 0 chrome sh -c 'cat > /etc/opt/chrome/policies/managed/download_safety.json << '\''EOF'\'' { "DownloadRestrictions": 0, "SafeBrowsingEnabled": false, "SafeBrowsingProtectionForDownloadEnabled": false } EOF ' # Kill Chrome so it restarts with the new policies docker exec -u 0 chrome pkill -f "chrome" || true ``` #### 3b. Extract the zips ```bash # install unzip if missing sudo apt install -y unzip mkdir -p /path/to/extracted for f in takeout-*.zip; do unzip -q -o "$f" -d extracted/ done ``` #### 3c. Import to Immich ```bash # From Google Takeout (preserves albums, metadata, people tags): /usr/local/bin/immich-go \ --server=http://:2283 \ --api-key= \ upload from-google-photos /path/to/extracted/ # From a plain folder (no Takeout metadata): /usr/local/bin/immich-go \ --server=http://:2283 \ --api-key= \ upload from-folder --recursive /path/to/folder/ ``` Using the Immich CLI inside the server container instead: ```bash docker exec immich_server /usr/src/app/server/bin/immich \ --url http://localhost:3001 \ --key \ upload --recursive /path/ ``` ### 4. Verify After import, check Immich web UI for: - Albums created (if using `from-google-photos`) - Asset counts - Background jobs processing (thumbnails, face detection, etc.) ## Pitfalls - **Flag names**: immich-go uses `--api-key` and `--server` (double-dash long form). Single-dash `-key` or `-server` gets parsed as short flags (e.g. `-k` + `ey=...`) and fails silently or shows help. - **API key permissions**: immich-go checks several endpoints during startup. A key needs **all** of these permissions or it will fail with 403 at each check: - `user.read` — connection validation (`GET /api/users/me`) - `server.about` — server info check (`GET /api/server/about`) - `asset.statistics` — pre-upload stats (`GET /api/assets/statistics`) - `asset.write` + `asset.read` — actual upload + duplicate detection - `job.create` — pausing background jobs during upload (optional — skip with `--pause-immich-jobs=FALSE`) **Best practice:** Create a fresh key and enable **all permissions**, or you'll hit a whack-a-mole of 403s one endpoint at a time. - **Chrome Safe Browsing**: Google Takeout zips can be 50GB+. Chrome flags them as "uncommon" and kills the download with `FILE_SECURITY_CHECK_FAILED` (reason 40, danger UNCOMMON). The `.crdownload` files will be orphaned — they will never complete. Fix: set managed policies to disable Safe Browsing for downloads, then restart Chrome. - **`unzip` not installed**: Check first. On Ubuntu/Debian: `sudo apt install unzip`. - **DB table names**: Immich PostgreSQL uses lowercase table names. The user table is `"user"`, not `"users"`. Column names use camelCase quoting (e.g. `"isAdmin"`, `"oauthId"`). **Key permissions vs --pause-immich-jobs flag:** If the API key lacks `job.create`, immich-go fails trying to pause background jobs. Workaround: add `--pause-immich-jobs=FALSE` to skip the job-pausing step: ```bash /usr/local/bin/immich-go \\ --server=http://:2283 \\ --api-key= \\ --pause-immich-jobs=FALSE \\ upload from-google-photos /path/to/extracted/ ``` This lets uploads proceed without admin-level job permissions. Thumbnails/face detection will process in the background normally.