tự host Immich: 6GB RAM và cách nâng cấp an toàn
Hướng dẫn xử lý lỗi exit 137 do thiếu RAM, cấu hình HTTPS cho port 2283 và cách sửa lỗi database pgvecto.rs khi nâng cấp lên Immich v3 để tránh mất dữ liệu.
Những gì bạn đang xây dựng
Immich là một dịch vụ backup ảnh và video tự host — một giải pháp thay thế thực thụ cho Google Photos. Nó có ứng dụng điện thoại tự động upload camera roll chạy ngầm, có timeline, album, nhận diện khuôn mặt và tìm kiếm bằng machine-learning để tìm các từ khóa như "beach" hoặc một người cụ thể mà không cần bạn phải tag thủ công. Bạn chạy nó trên VPS của riêng bạn, các file gốc được lưu trên disk của bạn, và không ai quét dữ liệu để quảng cáo cả.
Việc cài đặt gồm bốn container từ file Docker Compose của dự án. Bước này mất mười phút. Phần còn lại của hướng dẫn này là những phần khó khăn nhất: container machine-learning ngốn rất nhiều memory trên các máy cấu hình thấp, các file gốc chiếm dụng disk rất nhanh, ứng dụng mobile không chấp nhận server chạy HTTP thuần, và Immich thường xuyên có các thay đổi gây lỗi (breaking changes) khiến một docker compose pull bất cẩn có thể làm database không thể khởi động. Nếu bạn xử lý kỹ bốn vấn đề này, Immich sẽ hoạt động cực kỳ ổn định. Nếu bỏ qua, bạn sẽ mất cả một ngày cuối tuần để sửa lỗi.
Điều kiện tiên quyết và các lưu ý thực tế
- RAM: tài liệu chính thức yêu cầu tối thiểu 6 GB và khuyến nghị 8 GB — hãy coi 4 GB cộng với swap là mức tối thiểu tuyệt đối. Các container
immich-servervà Postgres chiếm ít tài nguyên. Containerimmich-machine-learningmới là thành phần ngốn tài nguyên nhất — nó load các model CLIP và face-recognition vào RAM để build index tìm kiếm, và trên máy 2 GB thì kernel sẽ kill nó. Hãy thêm swap ngay cả khi bạn có 4 GB. - Disk: hãy tính dung lượng cho toàn bộ thư viện của bạn, cộng thêm một khoảng dự phòng. Các file gốc sẽ được copy đầy đủ, cộng thêm việc Immich tạo thumbnail và ảnh preview (tốn thêm khoảng 10–20%). Một bộ sưu tập ảnh 200 GB sẽ cần một volume 300 GB. Postgres chiếm dung lượng rất nhỏ so với con số đó.
- CPU: bất kỳ KVM VPS hiện đại nào cũng ổn, nhưng chạy ML trên CPU sẽ chậm. Việc indexing smart-search cho một lượng lớn dữ liệu import có thể chạy trong nhiều giờ ở background. Điều này là bình thường; nó không bắt buộc phải có GPU.
- Một domain name trỏ về VPS. Mobile app ưu tiên sử dụng endpoint HTTPS, và bạn nên dùng một reverse proxy ở phía trước. Cách thiết lập này tương tự như cài đặt self-hosted Nextcloud với Docker, TLS và backups — Immich là giải pháp tương đương cho mảng hình ảnh của server lưu trữ file đó.
- Đã cài đặt Docker và Compose plugin — Docker Engine cùng với Compose v2 plugin từ apt repository chính thức của Docker, giống hệt như hướng dẫn trong hướng dẫn cơ bản về Docker Compose của chúng tôi.
Bước 1: Thêm swap trước khi làm bất cứ việc gì khác
Lỗi phổ biến nhất của Immich trên các VPS cấu hình thấp là container ML bị OOM-killed. Hãy cấp cho kernel một không gian đệm trước.
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h bây giờ sẽ hiển thị một dòng Swap: là 4.0Gi. Việc này không giúp ML chạy nhanh hơn, nhưng nó ngăn container bị crash khi đang index trên máy 4 GB.
Bước 2: Lấy file compose và env chính thức — hãy dùng bản gốc, đừng dùng bản copy
Immich cố định phiên bản các service và đặc biệt là image của database ngay trong các file đi kèm. Đừng copy file compose từ blog (kể cả bài này) để làm nguồn cấu hình chuẩn. Hãy tải các release assets:
sudo mkdir -p /opt/immich && cd /opt/immich
sudo wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
sudo wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.envCác file này lấy từ bản release đã được tag, nên các image reference sẽ khớp hoàn toàn. File compose định nghĩa bốn services, bạn nên biết chức năng của từng cái trước khi bắt đầu:
immich-server(ghcr.io/immich-app/immich-server, containerimmich_server) — API và web UI, lắng nghe tại port2283. Nó mount thư mục upload của bạn tại/data.immich-machine-learning(ghcr.io/immich-app/immich-machine-learning, containerimmich_machine_learning) — Tìm kiếm CLIP và nhận diện khuôn mặt. Nó cache các model đã tải về trong một volumemodel-cache. Đây là service tiêu tốn nhiều RAM nhất.database(containerimmich_postgres) — Postgres với extension vector VectorChord, dùng để chạy tính năng tìm kiếm tương đồng. Image tag được cố định bằng digest ngay trong file compose, ví dụghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:.... Các thiết lập cũ dùngpgvecto.rs; Immich v3.0 đã loại bỏ hỗ trợ này, nên mọi bản cài đặt hiện tại đều là VectorChord. Tuyệt đối không tự sửa tag này.redis(containerimmich_redis) — một instance Valkey/Redis để xử lý job queues.
Bước 3: Cấu hình .env — nơi lưu trữ ảnh và database của bạn
Mở .env và thiết lập bốn thông số. Mọi thứ bên dưới dòng đánh dấu đều giữ nguyên.
# Where original uploads are stored on the host
UPLOAD_LOCATION=/opt/immich/library
# Where the Postgres data lives. NEVER put this on an NFS/network share.
DB_DATA_LOCATION=/opt/immich/postgres
# "v3" is a floating tag that tracks the latest v3.x. Pin a full tag like
# v3.0.2 instead — then you upgrade on purpose, not by surprise.
IMMICH_VERSION=v3.0.2
# Change this to a long random string. Letters and digits only.
DB_PASSWORD=REPLACE_WITH_A_LONG_RANDOM_STRING
# Set your timezone so timestamps and "on this day" line up
TZ=Europe/London
###################################################################################
DB_USERNAME=postgres
DB_DATABASE_NAME=immichHai quy tắc giúp bạn tránh rắc rối. UPLOAD_LOCATION phải trỏ vào ổ đĩa lớn của bạn — nếu sau này bạn gắn thêm data volume, hãy thiết lập đường dẫn mount ngay từ đầu, vì nếu di chuyển sau đó, bạn sẽ phải di chuyển cả thumbnails và cập nhật lại các asset path. Và DB_DATA_LOCATION phải nằm trên local disk: Postgres sẽ bị corrupt nếu chạy trên NFS hoặc SMB share, tài liệu đã nêu rõ điều này. Nếu bạn chỉ sử dụng chữ cái và chữ số trong DB_PASSWORD, bạn sẽ tránh được một nhóm lỗi liên quan đến escaping connection-string.
Bước 4: Chạy lần đầu và tạo user admin
cd /opt/immich
sudo docker compose up -d
sudo docker compose psKết quả đúng sẽ là bốn container, tất cả đều ở trạng thái running và cuối cùng là healthy:
NAME STATUS
immich_machine_learning Up (healthy)
immich_postgres Up (healthy)
immich_redis Up (healthy)
immich_server Up (healthy)Lần up đầu tiên sẽ pull vài GB images, nên bạn hãy kiên nhẫn. Theo dõi tiến độ bằng sudo docker compose logs -f immich-server; server sẽ log thông báo đang lắng nghe tại port 2283 khi đã sẵn sàng. Bây giờ hãy mở http://YOUR_SERVER_IP:2283 trên trình duyệt. Lần truy cập đầu tiên sẽ hiển thị wizard Getting Started — tài khoản đầu tiên bạn tạo chính là admin. Hãy đặt mật khẩu mạnh; tài khoản này quản lý các thiết lập server, quản lý user và cấu hình ML mà bạn sẽ cần sau này.
Bước 5: Ứng dụng di động và backup chạy ngầm
Cài đặt "Immich" từ App Store hoặc Play Store. Tại màn hình đăng nhập, ứng dụng sẽ yêu cầu Server Endpoint URL. Hãy nhập đầy đủ URL bao gồm cả scheme, ví dụ: https://photos.example.com (ứng dụng sẽ tự động thêm /api). Đăng nhập bằng tài khoản bạn vừa tạo, sau đó mở màn hình Backup của ứng dụng, chọn các album cần bảo vệ (thường là Camera và Screenshots), và bật Background backup. Trên iOS, việc backup chạy ngầm bị giới hạn bởi OS — các bản upload khi đang mở ứng dụng (foreground) sẽ luôn chạy, còn bản upload chạy ngầm sẽ thực hiện khi OS cho phép.
Đây chính là bước mà nhiều người hay gặp lỗi, vì vậy hãy đọc Bước 6 trước khi bạn cố gắng xử lý ứng dụng.
Bước 6: HTTPS thông qua reverse proxy — và quy tắc full-URL
Ứng dụng mobile yêu cầu bắt buộc phải có HTTPS. Bạn hãy đặt một reverse proxy phía trước port 2283 và thực hiện terminate TLS tại đó. Nếu bạn đang chạy nhiều container, Traefik với automatic TLS cho nhiều Docker apps là lựa chọn gọn gàng nhất — chỉ cần một block label để route photos.example.com tới container immich-server và tự động lấy certificate cho bạn. Nếu bạn muốn dùng nginx, hướng dẫn Let's Encrypt với Certbot và nginx sẽ giúp bạn lấy certificate và cấu hình block proxy_pass http://127.0.0.1:2283;. Có một thiết lập proxy quan trọng đối với Immich: hãy tăng giới hạn upload size, vì video từ điện thoại rất lớn. Trong nginx, đó là tham số client_max_body_size 50000M; bên trong server block — giá trị mặc định 1 MB sẽ khiến việc upload video bị lỗi 413 Request Entity Too Large.
Quy tắc mà ứng dụng áp dụng: endpoint phải truy cập được và thực tế là phải dùng HTTPS. Các endpoint http://, hoặc dùng IP trực tiếp mà không có port, là nguyên nhân gây ra lỗi "the app cannot reach the server" — lỗi này được giải thích chi tiết ở phần dưới.
Bước 7: External libraries và uploads — import một cây thư mục ảnh có sẵn
Có hai cách để ảnh được đưa vào Immich, và chúng không giống nhau.
- Uploads là các asset do Immich quản lý. Ứng dụng hoặc web uploader sẽ copy file vào
UPLOAD_LOCATION. Immich có thể đổi tên, di chuyển và xóa chúng. - External libraries là các bản import chỉ đọc (read-only) của các file đã nằm sẵn trong một thư mục trên server của bạn — ví dụ như một cây thư mục
Picturescũ hoặc một bản export từ NAS. Immich sẽ index chúng tại chỗ và hiển thị trên timeline, nhưng không bao giờ sửa đổi hay xóa các file gốc.
Để import một cây thư mục có sẵn, hãy mount nó ở chế độ read-only vào trong container của server. Chỉnh sửa docker-compose.yml trong mục immich-server: và thêm một volume:
immich-server:
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
- /srv/photos:/mnt/media/photos:roTham số :ro đảm bảo Immich không bao giờ có thể tác động đến các file gốc. Khởi động lại container bằng sudo docker compose up -d, sau đó trên web UI, vào avatar của bạn → Administration → External Libraries → Create Library, chọn user sở hữu, nhấn Add tại mục Folders, và nhập đường dẫn container — tức là /mnt/media/photos, chứ không phải đường dẫn host /srv/photos. Nhấn Scan. Việc dùng đường dẫn host thay vì đường dẫn container là lỗi phổ biến nhất khi dùng external-library; kết quả là quá trình scan không tìm thấy gì và báo cáo zero assets.
Step 8: The upgrade discipline Immich demands
This is the part that separates a happy Immich from a broken one. Immich ships fast and does not backport fixes or support downgrades. Blindly tracking the floating v3 tag will eventually break your database. The discipline:
- Pin a version. Keep
IMMICH_VERSIONset to a concrete tag likev3.0.2, not the floatingv3that always pulls the newest v3.x. - Read the release notes every single time before upgrading. Breaking changes — especially database or vector-extension changes — are called out there. The v3.0 release is the obvious example: it removed pgvecto.rs outright, so anyone still on the old extension had to finish the VectorChord migration (introduced back in v1.133) before they could move up.
- Back up the database first (Step 9). Always, but doubly so when the notes mention the database.
- Take the new compose file too.
IMMICH_VERSIONonly pins the server and ML images. The Postgres image is pinned by digest insidedocker-compose.yml, so a version that needs a newer database extension ships a new compose file. Re-download both release assets, re-apply your.envvalues, then upgrade. - Update your mobile clients around the same time. The server only speaks its matching major version, and the app supports the current and previous major. A server that has jumped ahead of the app shows
Your app major version is not compatible with the server!on the phone until you update it, so it is safest to update the app first.
The actual commands, once you have the new files in place:
cd /opt/immich
sudo docker compose pull
sudo docker compose up -d
sudo docker image pruneBước 9: Backup — một bản dump database CỘNG VỚI các file gốc, và kiểm tra nó
Backup của Immich gồm hai thành phần, thiếu một trong hai thì bản backup đó vô dụng. database chứa cấu trúc album, khuôn mặt, index tìm kiếm và bản đồ ánh xạ từ asset đến file. thư mục originals chứa các ảnh thực tế. Nếu restore một thành phần mà thiếu thành phần kia, bạn sẽ nhận được kết quả là ảnh không có tổ chức hoặc một hệ thống trống rỗng trỏ vào các file không tồn tại.
Dump database bằng pg_dump từ bên trong container Postgres — cụ thể là database immich, không phải toàn bộ cluster:
sudo docker exec -t immich_postgres pg_dump --clean --if-exists \
--dbname=immich --username=postgres | gzip > /opt/immich/immich-db-$(date +%F).sql.gzSau đó backup UPLOAD_LOCATION — toàn bộ cây thư mục /opt/immich/library, đặc biệt là các thư mục con library/, upload/ và profile/ — bằng restic, rsync hoặc borg sang một máy khác hoặc object storage. Hãy thực hiện backup database trước và file sau, để bản dump không tham chiếu đến một ảnh mà bản backup file chưa kịp copy. Các external libraries cần được backup riêng biệt tại nguồn thực tế của chúng; Immich không quản lý chúng.
Bây giờ là phần mà mọi người thường bỏ qua: kiểm tra restore. Một bản restore phải được chạy trên một stack mới hoàn toàn chưa từng khởi động, trên một image Postgres có vector extension tương thích với bản dump — đây chính là lý do bạn không bao giờ được tùy tiện dùng image tag cho DB. Trên một máy trống có cùng compose và .env, hãy xóa mọi trạng thái cũ, chỉ khởi động database, sau đó load bản dump:
cd /opt/immich
sudo docker compose down -v
sudo docker compose pull
sudo docker compose create
sudo docker start immich_postgres
sleep 10
gunzip --stdout immich-db-2026-07-15.sql.gz |
sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" |
sudo docker exec -i immich_postgres psql --dbname=immich --username=postgres --single-transaction --set ON_ERROR_STOP=on
sudo docker compose up -dViệc rewrite search_path của sed là bắt buộc đối với database VectorChord — nếu bỏ qua, quá trình restore sẽ bị dừng giữa chừng. Khi stack khởi động lại với các file originals đã sẵn sàng, hãy mở web UI: nếu ảnh và album của bạn xuất hiện ở đó, bản backup của bạn đã thành công. Nếu bạn chưa bao giờ chạy thử bước này, bạn không có bản backup — bạn chỉ đang hy vọng.
Các lỗi thường gặp và các chuỗi ký tự bạn sẽ thấy
Container ML bị OOM-killed. sudo docker compose logs immich-machine-learning dừng đột ngột, docker compose ps hiển thị lỗi Restarting, và exit code là 137. sudo dmesg | grep -i oom xác nhận điều này: Out of memory: Killed process ... (python3). Các job search và face sau đó sẽ bị treo. Nguyên nhân là do thiếu RAM cho các model. Các cách khắc phục theo thứ tự: thêm swap (Bước 1); tăng thêm RAM cho VPS; hoặc nếu thực sự không thể, hãy tắt ML trong Administration → Settings → Machine Learning Settings bằng cách tắt Smart Search và Facial Recognition — bạn vẫn giữ được backups và albums, nhưng sẽ mất tính năng search-by-content. Việc xóa service immich-machine-learning khỏi file compose cũng cho kết quả tương tự.
Postgres không khởi động được sau khi upgrade. Server log lặp lại dòng như The database currently has VectorChord 0.5.3 activated, but the Postgres instance only has 0.4.2 available. This most likely means the extension was downgraded. — hoặc với các stack cũ hơn là The pgvecto.rs extension is not available in this Postgres instance.. Nguyên nhân là do image database có version extension cũ hơn version dữ liệu đã được upgrade, thường là do chỉnh sửa image tag bằng tay hoặc restore một bản dump mới hơn lên một image cũ hơn. Cách khắc phục là sử dụng đúng image Postgres tương ứng — hãy lấy file compose từ bản release khớp với database của bạn, không hạ cấp (downgrade), và chỉ restore lên image tương thích.
Mobile app không thể kết nối tới server. Màn hình login hiển thị lỗi kết nối / Server is not reachable sau khi bạn nhập URL. Có ba nguyên nhân: bạn nhập http:// trong khi proxy chỉ phục vụ https://; bạn kết nối trực tiếp tới backend nhưng quên không nhập port, khiến nó thử kết nối qua example.com (port 443) thay vì example.com:2283; hoặc reverse proxy không forward /api. Cách khắc phục là nhập đầy đủ URL https://photos.example.com và kiểm tra xem nó có load được trên trình duyệt điện thoại trước hay không. Nếu trình duyệt chạy được mà app không chạy được, có thể proxy đang strip path hoặc certificate là self-signed — app sẽ từ chối các cert không đáng tin cậy.
Hết dung lượng đĩa trong khi đang import. Việc upload bắt đầu lỗi, thumbnails bị trống, và logs hiển thị ENOSPC: no space left on device hoặc lỗi could not extend file ... No space left on device từ Postgres. df -h cho thấy volume UPLOAD_LOCATION đã đầy 100%. Đây là lý do bạn cần tính toán dung lượng đĩa trước khi import một thư viện lớn. Cách khôi phục là gắn thêm một volume lớn hơn, stop stack, di chuyển UPLOAD_LOCATION sang volume mới, cập nhật .env, và chạy lại — hoặc mở rộng đĩa hiện tại nếu nhà cung cấp của bạn cho phép. Postgres có thể bị kẹt nếu đĩa bị đầy, vì vậy hãy dọn dẹp không gian trống và restart container database trước khi kết luận là bị corrupt dữ liệu.
FAQ
Immich cần bao nhiêu RAM và disk?
Yêu cầu chính thức của Immich là tối thiểu 6 GB RAM và khuyến nghị 8 GB — mức thực tế cho thư viện nhỏ là 4 GB nếu có dùng swap. Bạn nên cấu hình swap vì container machine-learning thường xuyên bị spike. Về disk, hãy tính tổng dung lượng thư viện cộng thêm khoảng 10–20% cho thumbnails và previews trên local storage — tuyệt đối không đặt Postgres data directory trên network share. Nếu bạn đang cân nhắc các dịch vụ khác, hướng dẫn tự host các dịch vụ năm 2026 sẽ so sánh mức chiếm dụng tài nguyên của Immich với các dịch vụ khác.
Tôi có thể chạy Immich mà không cần GPU không?
Có. Container machine-learning chạy bình thường trên CPU — GPU chỉ giúp tăng tốc indexing smart-search và transcoding video nếu dùng đúng image variant. Khi dùng CPU, việc indexing ban đầu cho thư viện lớn có thể mất vài giờ chạy ngầm, nhưng không làm gián đoạn việc backup hay duyệt ảnh. Nếu máy của bạn quá yếu để chạy ML, bạn có thể tắt Smart Search và Facial Recognition trong admin settings và giữ lại các tính năng khác.
Làm sao để upgrade Immich một cách an toàn?
Hãy pin IMMICH_VERSION vào một tag cụ thể như v3.0.2, đọc release notes trước mỗi lần upgrade và phải backup database trước. Vì image Postgres được pin bên trong docker-compose.yml thay vì bằng IMMICH_VERSION, hãy tải lại cả compose file và example.env từ release mục tiêu rồi áp dụng lại các giá trị của bạn, sau đó chạy docker compose pull && docker compose up -d. Đừng bao giờ để version ở dạng float — Immich có các breaking changes và không hỗ trợ downgrade.
Tôi cần backup chính xác những gì?
Cần hai thứ đi kèm nhau: một pg_dump của immich database và toàn bộ thư mục UPLOAD_LOCATION originals. Database chứa albums, faces và mapping giữa asset với file; thư mục chứa ảnh thực tế. Việc restore yêu cầu cả hai cộng với một database image có extension vector tương thích. Hãy thực hiện database dump trước và copy file sau, đồng thời hãy test restore trên một máy trống ít nhất một lần — một bản backup chưa được test thì không được coi là bản backup.
Làm sao để import thư mục ảnh có sẵn?
Mount thư mục đó ở chế độ read-only vào container immich-server dưới dạng một volume bổ sung (ví dụ - /srv/photos:/mnt/media/photos:ro), recreate container, sau đó vào Administration → External Libraries để tạo một library và thêm đường dẫn container /mnt/media/photos. Immich sẽ index các file tại chỗ và không bao giờ sửa đổi hay xóa chúng. Lỗi phổ biến nhất là nhập đường dẫn host thay vì đường dẫn container, khiến việc scan không tìm thấy gì.