{"slug": "immich-sizing-guide-what-a-20000-photo-google-takeout-import-really-costs-in-ram", "title": "Immich Sizing Guide: What a 20,000 Photo Google Takeout Import Really Costs in Disk, RAM and Hours", "summary": "A developer's sizing guide for the self-hosted photo app Immich reveals that importing a 20,000-photo Google Takeout library requires 1.35 to 1.6 times the raw photo size on disk, 6 GB of RAM with machine learning enabled, and a full weekend of background processing. The guide highlights that storage templates, machine learning models, and transcoding policies set early force full re-runs if changed later, and warns of silent OOM killer loops that can stall imports.", "body_md": "Budget roughly 1.35 to 1.6 times your raw photo size on disk, 6 GB of RAM for the full stack with machine learning enabled, 4 CPU cores, and a full weekend of background processing before a 20,000 item library is fully searchable. The import itself is the fast part. Thumbnail generation, video transcoding and machine learning jobs are what actually keep the server busy, and three settings decided in the first hour, the storage template, the machine learning model, and the transcoding policy, are the ones that force a full re-run of every job if you change your mind in month two.\n\n**TL;DR by reader profile:**\n\nThe central tradeoff is that every setting which makes Immich feel fast and searchable later, larger machine learning models, generated previews, transcoded video, costs you disk and hours of CPU during the first week, and changing your mind afterwards means paying that cost a second time.\n\nStart from the size of your Google Takeout, not the photo count. A 20,000 item library from a modern phone is usually somewhere between 60 GB and 120 GB of originals, because a 12 megapixel HEIC frame lands near 2 MB while a single 4K clip can pass 400 MB. Immich stores your originals untouched, then adds derived files on top.\n\nThe multiplier you should plan for is 1.35x to 1.6x the original size, split across four directories under `UPLOAD_LOCATION`\n\n:\n\n`upload/`\n\nand `library/`\n\n, the originals:`thumbs/`\n\n, the previews:`encoded-video/`\n\n, the transcodes:Practical floor: if your Takeout unzips to 90 GB, provision 200 GB and do not let the filesystem cross 80 percent during import. Running `du -sh`\n\non each subdirectory after the first 1,000 assets gives you a real multiplier for your own library, which beats any generic estimate.\n\nBecause Immich does not generate one thumbnail per photo. It generates a small WebP tile for the timeline grid and a much larger preview used whenever you open an asset, and that second file is the one that surprises people. A 2 MB HEIC original can produce a preview of several hundred kilobytes, so the ratio between a phone photo and its derivatives is far worse than it is for a 25 MB RAW file, where the same preview is a rounding error.\n\nFour things inflate these directories beyond a naive estimate:\n\nCheck the real split with `du -sh thumbs encoded-video`\n\ninside `UPLOAD_LOCATION`\n\nbefore you conclude anything about total size.\n\nPlan for 6 GB of system RAM for the full stack with machine learning enabled, and 4 GB if you disable smart search. The four containers are not equal: the server and Postgres are steady and modest, Redis is tiny, and `immich-machine-learning`\n\nis the one that spikes, because it loads a model into memory the first time a job needs it and holds it there.\n\nWhat failure looks like is the part nobody warns you about. The kernel OOM killer does not stop Immich politely. It kills the machine learning container mid job, the job returns to the queue, the container restarts, reloads the model, and fails again on the same asset. You get a silent loop that burns CPU for hours and never advances the counter.\n\nThree signals tell you this is happening:\n\n`docker compose ps`\n\nshows a restart count climbing:`dmesg | grep -i oom`\n\nprints kill lines:The fix is either more RAM, a smaller model, or fewer concurrent machine learning jobs, set to 1 in the job settings.\n\nHosting choice matters here because you cannot add RAM to a box that has none free. A NAS with 4 GB shared with other services, a self-managed VPS, a mini PC and a managed option are all viable starting points. Yundera is a managed Personal Cloud Server, built on CasaOS, that runs self-hosted apps as Docker containers on a server dedicated to the user.\n\nAll of them, unless you tell Immich otherwise. Immich runs background work as named job queues, and each queue has its own concurrency setting in Administration, Settings, Job Settings. The defaults are tuned for a machine that is doing nothing else, so on a 4 core box the first import will pin every core for hours and make the web interface feel sluggish while you are still uploading.\n\nThe queues do not weigh the same:\n\nFour cores is a workable floor. Two cores works but roughly doubles your wall clock time, and a shared vCPU with a burst credit balance will collapse to baseline speed partway through and stay there.\n\nWatch it live with `docker stats`\n\n, which shows per container CPU percentage. If `immich-server`\n\nsits near your core count times 100 percent for hours, that is normal during first import, not a fault. Lower Thumbnail Generation concurrency to 2 if you need the machine responsive for anything else.\n\nThink in phases, not in one number. The upload finishes long before the library is usable, and the gap between those two moments is where people assume something is broken.\n\n| Phase | What is happening | Typical shape on 4 cores |\n|---|---|---|\nUpload via `immich-go` or the CLI |\nFiles transferred, metadata read, duplicates skipped | Minutes to a few hours, limited by disk or LAN speed |\n| Metadata extraction | Dates, GPS, camera fields written to Postgres | Completes soon after upload, rarely the bottleneck |\n| Thumbnail generation | Two derivatives written per asset | Several hours, and the timeline stays gappy until it ends |\n| Video transcoding | Non compatible clips re-encoded by FFmpeg | Hours to days, entirely driven by how much video you have |\n| Smart search and face detection | Embeddings and faces computed per asset | The long tail, often the last queue still running |\n\nA photo heavy library with under 500 short clips is usually fully processed inside 24 hours on 4 cores. Add 3,000 camcorder clips and the same library can take a long weekend, because transcoding runs at concurrency 1 by design.\n\nTwo practical points. First, upload speed and processing speed are independent: `immich-go`\n\ncan finish at 2 a.m. while the job queues still have 18,000 items pending. Second, the Jobs page in the admin panel shows an active and a waiting count per queue, and the waiting count falling is the only honest progress bar you have.\n\nDo not judge the install until every queue reads zero. Search results, people grouping and the map view are all incomplete before that point, and re-running them costs the same hours again.\n\nUpload if Immich is becoming your primary photo home. Use an external library if you already have a folder tree you edit with other tools and want to keep owning.\n\nThe two paths differ in who controls the files on disk:\n\n`UPLOAD_LOCATION`\n\nand obey the storage template:`/mnt/photos`\n\nin the container, Immich indexes what it finds, generates thumbnails into its own directories, and never writes to or renames your originals.`immich-go`\n\nand also mounting that same folder externally gives you every asset twice, counted twice in storage reporting.For a 20,000 file RAW archive averaging 25 MB, the external route is the safer first week choice: your originals stay where your backup script already finds them, and a mistake in Immich cannot rename or move 500 GB of files.\n\nTakeout does not hand you a photo library. It hands you archives of files whose metadata has been moved out of the images and into JSON sidecars, and if you import them naively your entire timeline collapses onto the import date.\n\n`.json`\n\nholding `photoTakenTime`\n\nand location, and Immich's plain upload path does not merge them, which is why so many first imports show 20,000 photos all dated today.`IMG_1234(1).jpg`\n\n, so naive pairing by filename fails on a meaningful slice of any large export.`-edited`\n\nvariant are two assets, which inflates your item count above what Google Photos showed you.The fix is tooling, not manual cleanup. `immich-go`\n\nwas written for exactly this: it reads the sidecars, rebuilds dates, recreates albums and pairs motion photos, and it can consume the Takeout zip files directly without you unzipping 100 GB first.\n\nImport a single archive as a test run of a few hundred assets, confirm the timeline dates look correct, then delete those assets and run the full set. Discovering a date problem after 20,000 items means redoing all of it.\n\nDecide this before a single asset lands, because the template controls the on disk path of every uploaded file, and changing it later triggers a Storage Migration job that physically moves all 20,000 files.\n\nTemplates are built from variables such as `{{y}}`\n\n, `{{MM}}`\n\n, `{{filename}}`\n\nand `{{ext}}`\n\n, set in Administration, Settings, Storage Template. Four sane choices:\n\n| Template | Resulting path shape | Best for |\n|---|---|---|\n| Disabled, the default | Random directory and asset id under the user folder | People who will never touch the files outside Immich |\n`{{y}}/{{MM}}/{{filename}}` |\n`2019/07/IMG_1234.jpg` |\nAnyone who wants a browsable archive that survives Immich |\n`{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}` |\n`2019/2019-07-14/IMG_1234.jpg` |\nEvent heavy libraries where one day equals one shoot |\n`{{album}}/{{filename}}` |\n`Corsica 2019/IMG_1234.jpg` |\nAlbum driven workflows, with the caveat that assets in no album fall back |\n\nThe default is genuinely defensible. Random paths never collide and never break when you rename an album. The argument against it is portability: if Immich is your only index, a corrupted database leaves you with a directory of meaningless filenames.\n\nTwo hazards. Filename collisions inside the same folder get a numeric suffix, so `{{y}}/{{filename}}`\n\non a phone that resets its counter will produce `IMG_0001_1.jpg`\n\n. And the template applies to uploaded assets only, never to external libraries, which keep their original paths untouched.\n\nPick a year and month layout unless you have a specific reason not to. It is readable, it sorts, and it means a plain file browser can still make sense of your archive years from now.\n\nThis is the setting with the harshest change penalty. Smart search stores one embedding per asset, and embeddings from different models are not interchangeable, so switching models invalidates all 20,000 of them and forces a full re-run of the Smart Search queue.\n\nThe choice lives in Administration, Settings, Machine Learning Settings, and the models are pulled from the Immich Hugging Face collection on first use.\n\nTest with 200 assets before committing. Search for three things you would realistically look for, judge the results, then import the rest. Making that judgement after the full import costs you the entire queue again.\n\nLeave it on the default optimal policy unless you have a specific reason not to, then understand exactly what that policy is doing to your disk and your weekend.\n\n| Transcode policy | What it re-encodes | What it costs you |\n|---|---|---|\n| Don't transcode | Nothing | Zero extra disk, but clips your browser cannot decode simply will not play |\n| Videos not in an accepted format | Only unsupported codecs and containers | The cheapest useful option, ideal for phone footage that is already H.264 in MP4 |\n| Videos higher than target resolution or not in an accepted format | The above, plus anything above the target resolution, 720p by default | The default, and the reason a 4K library balloons in processing time |\n| All videos | Every clip regardless of source | Doubles your video storage and can run for days on 3,000 items |\n\nThree things decide the real cost. The target resolution, because raising it to 1080p produces larger transcodes but keeps quality your phone actually shot. The constant rate factor, where the default of 23 trades size against fidelity. And hardware acceleration, which is the difference between hours and days: Immich supports NVENC, QSV, VAAPI and RKMPP, enabled by adding `hwaccel.transcoding.yml`\n\nto your compose command and selecting the matching API in the settings.\n\nThe trap is that the original is always kept. Transcodes are additional files in `encoded-video/`\n\n, never replacements, so an aggressive policy costs storage on top of your originals rather than saving any.\n\nIf your video is mostly modern phone footage, switching to \"videos not in an accepted format\" before the first import can remove most of the transcoding queue entirely.\n\nThe hardware question is really a question about video and about who fixes it at 11 p.m. when the phone app stops backing up.\n\nWhatever you pick, the deciding number is free RAM, not disk. Disk you can add later. A 4 GB ceiling shared with three other containers is what actually stops a 20,000 item import.\n\nSome Immich settings are free to change at any time. Five are not, and the difference is whether the change invalidates work already written to disk. On a 20,000 item library, each of these costs you the same hours you spent during the first import.\n\n| Setting | What changing it later triggers | Safe default for week one |\n|---|---|---|\n| Storage template | A Storage Migration job that moves every uploaded file on disk | Set it before the first upload, then leave it |\n| Smart search model | All existing embeddings discarded, full Smart Search queue re-run | Pick the model you can afford to run, test on 200 assets |\n| Preview and thumbnail size | Thumbnail Generation re-runs for every asset, old derivatives replaced | Accept the default unless you view photos on a 4K display |\n| Transcode policy or target resolution | Every clip matching the new rule is re-encoded from the original | Decide by looking at what codecs your clips actually use |\n| Facial recognition model | Face detection and recognition re-run, and people you named can need reassigning | Enable it once and do not switch models casually |\n\nTwo settings that look scary and are not: `UPLOAD_LOCATION`\n\ncan be moved if you move the directory and keep the structure intact, and job concurrency can be raised or lowered mid import with no penalty at all.\n\nThe practical rule is simple. Anything that changes how a derived file is generated forces regeneration of every derived file. Anything that changes scheduling or naming of the running system does not.\n\nWrite your five choices down before you import. Reviewing them takes 15 minutes. Discovering one was wrong in month two costs you another weekend of queued jobs.", "url": "https://wpnews.pro/news/immich-sizing-guide-what-a-20000-photo-google-takeout-import-really-costs-in-ram", "canonical_source": "https://dev.to/john_182319291/immich-sizing-guide-what-a-20000-photo-google-takeout-import-really-costs-in-disk-ram-and-hours-48ic", "published_at": "2026-08-29 07:05:56+00:00", "updated_at": "2026-08-29 07:49:05.812497+00:00", "lang": "en", "topics": ["developer-tools", "ai-infrastructure"], "entities": ["Immich", "Google Takeout", "Yundera", "CasaOS", "Postgres", "Redis"], "alternates": {"html": "https://wpnews.pro/news/immich-sizing-guide-what-a-20000-photo-google-takeout-import-really-costs-in-ram", "markdown": "https://wpnews.pro/news/immich-sizing-guide-what-a-20000-photo-google-takeout-import-really-costs-in-ram.md", "text": "https://wpnews.pro/news/immich-sizing-guide-what-a-20000-photo-google-takeout-import-really-costs-in-ram.txt", "jsonld": "https://wpnews.pro/news/immich-sizing-guide-what-a-20000-photo-google-takeout-import-really-costs-in-ram.jsonld"}}