SSD Nodes Learn Hosting plans →
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-10-26

Cách backup và restore Immich trên VPS đúng cách

Backup Immich phải gồm UPLOAD_LOCATION, SQL dump Postgres và file cấu hình. Xem vì sao copy data directory đang chạy không đủ và lỗi restore làm timeline trống.

Nội dung bắt buộc của một bản backup Immich

Một bản backup Immich gồm ba phần được ghi lại cùng một thời điểm. Các file gốc trong UPLOAD_LOCATION. SQL dump của database Postgres. .env và docker-compose.yml mô tả stack. Khôi phục nghĩa là replay dump đó vào một database mới trong khi Immich server đang dừng, rồi chỉ khởi động phần còn lại của stack sau bước này. Nếu làm sai thứ tự, bạn sẽ có một Immich hoạt động nhưng timeline trống, trong khi ổ đĩa vẫn chứa đầy dữ liệu.

Việc tách thành hai phần này rất quan trọng vì Immich lưu state ở hai nơi không biết gì về nhau. Postgres chứa mọi album, mọi face cluster, mọi shared link, mọi user account và API key, cùng path đã lưu của từng asset. Filesystem chứa các pixel. Khôi phục file mà không có database thì Immich không hiển thị gì. Khôi phục database mà không có file thì mọi asset đều mở thành ảnh lỗi.

Các lệnh ở đây được viết cho Immich v3.1.0, release hiện tại vào đầu tháng 8 năm 2026. Tính đến 2026-10-25, release hiện tại là v3.3.5, một security patch được phát hành ngày 2026-10-21. Compose file, database image và các bước backup, restore được tài liệu hóa của bản này khớp với nội dung trên trang này. Project phát hành nhanh và quy trình backup được tài liệu hóa đã thay đổi nhiều lần, vì vậy hãy kiểm tra version bạn đang thực sự chạy trước khi sao chép bất kỳ nội dung nào. Nếu stack chưa chạy, hãy bắt đầu với hướng dẫn cài đặt Immich rồi quay lại đây.

Biết đường dẫn của bạn trỏ đến đâu

Hai biến trong .env quyết định mọi thứ trên trang này. UPLOAD_LOCATION là thư mục cha mà Immich ghi toàn bộ media vào. DB_DATA_LOCATION là thư mục dữ liệu Postgres.

example.env mặc định đặt UPLOAD_LOCATION=./library. Đây là giá trị mặc định dễ gây nhầm lẫn, vì sau đó Immich tạo một thư mục tên library bên trong thư mục này. Các file gốc của bạn nằm tại ./library/library. Hãy đặt một đường dẫn tuyệt đối để script backup không bao giờ phụ thuộc vào thư mục mà bạn chạy script.

UPLOAD_LOCATION=/srv/immich/data
DB_DATA_LOCATION=/srv/immich/postgres
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
IMMICH_VERSION=v3.1.0

Bên trong UPLOAD_LOCATION, Immich tạo một số thư mục. Ba thư mục trong số đó chứa dữ liệu mà không job nào có thể tạo lại:

  • library: các file gốc, được sắp xếp theo storage template của bạn
  • upload: các file gốc chưa được chuyển vào bố cục của template, cùng với các file đang được upload
  • profile: ảnh profile của người dùng

Nếu mất library thì ảnh đó cũng mất. Immich không lưu bản sao thứ hai của file gốc ở bất kỳ đâu.

Vì sao sao chép thư mục dữ liệu Postgres không phải là backup

DB_DATA_LOCATION có vẻ là mục tiêu dễ sao chép. Đây là một thư mục, rsync sẽ sao chép nó, và lệnh sao chép hoàn tất mà không báo lỗi. Nếu bạn sao chép trong khi Postgres đang chạy, bản sao đó không phải là backup. Nếu bạn sao chép khi Postgres đã dừng, đó là một backup, nhưng rất dễ hỏng. Dưới đây là hai lý do, và bạn có thể theo dõi cả hai trường hợp lỗi này.

Lý do đầu tiên là dữ liệu bị xé lệch giữa các file. Trước tiên, Postgres ghi mọi thay đổi vào write-ahead log (WAL), rồi mới áp dụng chúng vào các table file tại checkpoint. Vì vậy, ở bất kỳ thời điểm nào, các file trên disk cũng có thể đang được cập nhật dở. Một lần sao chép tuần tự mất bốn phút có thể đọc file đầu tiên lúc 02:00 và file cuối cùng lúc 02:04. Hai file đó không thuộc cùng một transaction. Khi khởi động Postgres với kết quả này, nó sẽ từ chối khởi động với PANIC: could not locate a valid checkpoint record, hoặc khởi động rồi dừng ở lần đọc đầu tiên một page bị hỏng với invalid page in block 1234 of relation base/16384/.... Không cách nào khôi phục từ bản sao đó.

Lý do thứ hai liên quan đến bản sao được tạo khi mọi thứ đã dừng. PostgreSQL xác nhận rằng bản sao hoàn chỉnh của data directory đã shutdown là một backup hợp lệ, nhưng directory đó chỉ mở được bằng các binary dùng cùng storage format. Immich cố định database image bằng digest, hiện là ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0. Đây là Postgres 14 tích hợp sẵn các extension tìm kiếm vector VectorChord, pgvector và pgvecto.rs. Hiện Immich chạy trên VectorChord; pgvecto.rs chỉ còn trong image để các bản cài đặt cũ có thể migrate khỏi nó và các backup cũ vẫn có thể được restore. Một minor release mới hơn của Postgres 14 có thể mở directory mà không thay đổi, nhưng một major version Postgres khác thì không. Các extension mà table của bạn phụ thuộc vào cũng phải có mặt và dùng version tương thích. Host restore phải chạy image tương thích. SQL dump không gặp vấn đề này: nó là văn bản, và bất kỳ server tương thích nào cũng có thể replay nó.

pg_dump loại bỏ hoàn toàn vấn đề dữ liệu bị xé lệch. Nó đọc toàn bộ database trong một MVCC (multi-version concurrency control) snapshot duy nhất, nên thấy database đúng như tại một thời điểm trong khi các thao tác ghi khác vẫn tiếp tục. Vì vậy, bạn không cần dừng Postgres để tạo dump.

Những gì có thể bỏ khỏi backup

Các thành phần này sẽ được tạo lại, nên bạn có thể bỏ qua:

  • thumbs: ảnh preview và thumbnail
  • encoded-video: video đã transcode
  • DB_DATA_LOCATION: được tạo lại từ dump
  • Docker volume model-cache: machine learning model, sẽ được tải lại khi cần

Bỏ qua chúng là một lựa chọn đánh đổi, không phải lợi ích miễn phí. Việc tạo lại thumbnail và transcode cho một thư viện lớn có thể ngốn hàng giờ CPU trên một VPS nhỏ, trong thời gian đó timeline sẽ hiển thị placeholder màu xám. Bạn chạy lại các job này từ Administration > Jobs, với "Generate Thumbnails" và "Transcode Videos" được đặt chạy trên các asset còn thiếu. Nếu backup target còn đủ chỗ, hãy giữ chúng để không phải chờ rebuild. Nếu sắp hết dung lượng lưu trữ, hãy bỏ chúng và lên kế hoạch rebuild. Định cỡ thư viện Immich giải thích các folder này tăng kích thước như thế nào so với file gốc.

Có thêm một folder bạn nên biết. UPLOAD_LOCATION/backups chứa các database dump tự động của Immich, được ghi hằng ngày lúc 02:00 và giữ lại 14 bản gần nhất; bạn có thể cấu hình tại Administration > Settings > Database Dump Settings. Chúng không tốn thêm gì và thực sự hữu ích. Tuy nhiên, chúng nằm trên cùng disk với thư viện mà chúng bảo vệ, nên chỉ giúp xử lý migration lỗi chứ không giúp ích khi server hỏng hoàn toàn. Dù vậy, bạn vẫn nên tự tạo dump, vì dump do bạn chủ động chạy sẽ được tạo đúng lúc với file snapshot tương ứng.

Tạo database dump

docker exec -t immich_postgres pg_dump --clean --if-exists \
  --dbname=immich --username=postgres \
  | gzip > /srv/immich/backup/immich.sql.gz

Thay immich và postgres bằng DB_DATABASE_NAME và DB_USERNAME nếu bạn đã thay đổi chúng. --clean --if-exists thêm DROP ... IF EXISTS trước mỗi CREATE, để dump được phát lại vào database đã có sẵn các object thay vì dừng ở object đầu tiên.

Bây giờ là chi tiết thường âm thầm làm hỏng các backup script. Lệnh đó là một pipeline, và shell dùng exit status của lệnh cuối cùng trong pipeline. Nếu pg_dump fail do sai password hoặc container chưa chạy, gzip sẽ nhận một stream rỗng, ghi ra một file gzip hoàn toàn hợp lệ rồi exit với mã 0. Script của bạn ghi nhận thành công, còn bạn có một backup 20-byte. Đặt pipefail ở đầu mọi backup script:

#!/usr/bin/env bash
set -euo pipefail

Sau đó kiểm tra kết quả thay vì tin vào exit code:

ls -lh /srv/immich/backup/immich.sql.gz
gunzip -c /srv/immich/backup/immich.sql.gz | head -n 3

Dòng đầu tiên của một dump hợp lệ là -- PostgreSQL database dump. File chỉ vài trăm byte là dump bị lỗi, bất kể script báo gì.

Ghi lại release đã tạo dump ngay bên cạnh file dump bằng cách truy vấn server đang chạy:

curl -fsS http://localhost:2283/api/server/version > /srv/immich/backup/immich-version.txt

Endpoint đó không cần login và trả về release đang chạy dưới dạng số major, minor và patch, nên 3, 1 và 0 có nghĩa là v3.1.0. Không dùng .env cho việc này, cũng không dùng docker inspect --format '{{.Config.Image}}' immich_server vì nó chỉ echo image reference mà bạn đã cấu hình. File mặc định đặt IMMICH_VERSION=v3, một floating tag luôn theo release 3.x mới nhất, nên bản ghi kết thúc bằng :v3 không cho biết build nào thực sự đã tạo dump. Đồng thời pin exact tag trong .env.

Tạm dừng server, sau đó tạo snapshot bằng restic

Các file trong UPLOAD_LOCATION không bất biến khi Immich đang chạy. Server ghi các lượt upload mới, còn job storage template di chuyển file giữa các thư mục. Nếu công cụ backup đọc một file khi file đó đang được ghi dở, nó sẽ lưu các byte đã đọc như thể đó là toàn bộ file, và không thành phần nào báo lỗi. Dừng server container trong suốt thời gian chạy:

docker stop immich_server

Để immich_postgres tiếp tục chạy vì dump cần đến nó. Web interface và mobile app sẽ offline cho đến khi bạn khởi động lại server. Với một instance dùng trong gia đình, việc này thường không thành vấn đề nếu chạy lúc 03:00.

restic phù hợp ở đây vì nó deduplicate và encrypt dữ liệu trước khi bất kỳ dữ liệu nào rời khỏi máy. Trỏ nó đến một repository không nằm trên server này:

export RESTIC_REPOSITORY=sftp:backup@backup.example.com:/srv/restic/immich
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init

Object storage cũng hoạt động tương tự và là lựa chọn tốt hơn nếu bạn muốn bản sao hoàn toàn nằm ngoài phần cứng của mình:

export RESTIC_REPOSITORY=s3:https://s3.example.com/immich-backup
export AWS_ACCESS_KEY_ID=your-access-key
export AWS_SECRET_ACCESS_KEY=your-secret-key
restic init

Endpoint đó có thể là một MinIO bucket do bạn tự chạy trên máy thứ hai hoặc bất kỳ nhà cung cấp nào tương thích với S3. Repository trên cùng disk với thư viện chỉ bảo vệ bạn trước thao tác xóa nhầm, chứ không bảo vệ được trước bất kỳ sự cố nào khác.

Sau đó tạo snapshot, chỉ định chính xác những gì cần backup:

restic backup \
  /srv/immich/backup/immich.sql.gz \
  /srv/immich/backup/immich-version.txt \
  /srv/immich/data/library \
  /srv/immich/data/upload \
  /srv/immich/data/profile \
  /srv/immich/.env \
  /srv/immich/docker-compose.yml
docker start immich_server

restic đọc toàn bộ cây thư mục trong mỗi lần chạy nhưng chỉ upload các block chưa từng xuất hiện trước đó. Vì vậy, snapshot đầu tiên sẽ chuyển toàn bộ thư viện, còn mỗi snapshot sau đó chỉ chuyển các ảnh mới trong ngày.

Retention và các key phải lưu ở nơi khác

restic forget --prune --keep-daily 7 --keep-weekly 5 --keep-monthly 12

forget loại snapshot khỏi index. --prune là phần xóa dữ liệu mà các snapshot đó từng là tham chiếu cuối cùng. Chạy forget mà không có --prune thì chi phí lưu trữ sẽ không bao giờ giảm.

Kiểm tra cấu trúc không tốn nhiều tài nguyên, nên hãy chạy mỗi tuần một lần:

restic check

Lệnh này xác minh metadata của repository nhất quán. Nó không đọc dữ liệu của bạn. Mỗi tháng một lần, hãy đọc lại một mẫu dữ liệu và đối chiếu với các hash đã ghi nhận:

restic check --read-data-subset=5%

Đây là cách kiểm tra duy nhất phát hiện lỗi hỏng dữ liệu âm thầm trên storage backend, vì nó tải các block thực tế xuống và tính lại checksum. Chạy --read-data đầy đủ trên thư viện ảnh nghĩa là tải toàn bộ repository xuống. Với object storage tính phí theo dung lượng truyền, việc này tốn tiền thực tế, nên subset luân phiên mới là cách mọi người thường dùng.

Bây giờ là phần nhiều người bỏ qua. Không thể khôi phục mật khẩu của restic repository. Không có cách reset và cũng không có support ticket. Nếu bản sao duy nhất nằm trong /root/.restic-password trên chính server bạn đang cố restore, backup của bạn chỉ còn là dữ liệu đã mã hóa không thể sử dụng. Điều tương tự cũng áp dụng cho access key của object storage và DB_PASSWORD từ .env. Hãy lưu tất cả những thứ này ở nơi không phụ thuộc vào việc máy này còn hoạt động: in ra giấy và cất trong ngăn kéo, hoặc lưu trong password manager chạy trên phần cứng khác. Nếu password manager đó cũng được self-host, nó cũng cần được xử lý như vậy, và backup Vaultwarden là một công việc riêng.

Khôi phục Immich theo đúng thứ tự

Thứ tự restore quyết định một bản backup tốt có biến thành timeline trống hay không. Hãy làm theo trình tự này trên host mới.

Khôi phục config trước. Config cho biết cần chạy version nào và các path trỏ đến đâu.

restic restore latest --target /restore \
  --include /srv/immich/.env \
  --include /srv/immich/docker-compose.yml \
  --include /srv/immich/backup

Pin version trước khi khởi động bất kỳ thứ gì. Đọc immich-version.txt, đặt IMMICH_VERSION trong .env thành đúng release đó (v3.1.0 cho major 3, minor 1, patch 0), và tạm thời không dùng release mới nhất. Immich không hỗ trợ downgrade, kể cả giữa các patch release, nên nếu server mới hơn khởi động với dump cũ rồi chạy migration, bạn sẽ không thể quay lại.

Khôi phục media.

restic restore latest --target /restore --include /srv/immich/data

Sau đó di chuyển library, upload và profile để chúng nằm trực tiếp bên trong vị trí mà UPLOAD_LOCATION trỏ đến trên host này. Host path có thể thay đổi vì compose file bind directory đó vào một path cố định bên trong container. Layout bên trong directory đó thì không được thay đổi.

Chỉ khởi động database. Để DB_DATA_LOCATION trống để Postgres khởi tạo một cluster mới.

cd /srv/immich
docker compose pull
docker compose create
docker start immich_postgres
docker exec immich_postgres pg_isready --username=postgres

pg_isready in accepting connections sau khi quá trình setup lần đầu hoàn tất; quá trình này mất vài giây. docker compose create build mọi container nhưng không khởi động chúng, và đó chính là mục đích của bước này: Immich server chưa được phép chạy. Một server khởi động với database trống sẽ chạy migration, tạo schema mới và yêu cầu bạn tạo admin account mới. Khi đó bạn đang nạp dump vào bên dưới một application đang chạy.

Nạp lại dump.

gunzip --stdout /restore/srv/immich/backup/immich.sql.gz \
  | sed "s/SELECT pg_catalog.set_config('search_path', '', false);/SELECT pg_catalog.set_config('search_path', 'public, pg_catalog', true);/g" \
  | docker exec -i immich_postgres psql --dbname=immich --username=postgres \
      --single-transaction --set ON_ERROR_STOP=on

Trong đó có 2 phần thực sự quyết định kết quả. sed tồn tại vì pg_dump ghi một search_path trống vào output như một biện pháp an toàn, để các tên không có schema chỉ định trong dump không thể trỏ nhầm đến một schema ngoài dự kiến. Các vector-search type của Immich nằm trong public, nên khi search path trống, quá trình restore gặp column đầu tiên khai báo bằng vector type thì psql dừng với ERROR: type "vector" does not exist. Thêm lại public vào path sẽ khắc phục lỗi này.

--single-transaction --set ON_ERROR_STOP=on bọc toàn bộ quá trình restore trong một transaction và dừng ngay khi gặp lỗi đầu tiên. Bạn sẽ nhận được database hoàn chỉnh hoặc database chưa bị thay đổi. Nếu không dùng tùy chọn này, lỗi giữa chừng sẽ để lại một database vẫn khởi động được, vẫn chấp nhận login của bạn, nhưng thiếu một số lượng album không xác định. Bạn có thể chỉ phát hiện ra việc này sau nhiều tuần.

Bây giờ khởi động toàn bộ stack.

docker compose up -d
docker compose ps
docker logs -f immich_server

Chờ một startup line như Immich Server is listening on, sau đó mở port 2283 và đăng nhập bằng credential cũ, vì user account đã được khôi phục cùng dump. Nếu trang login lại yêu cầu tạo admin account đầu tiên, database chưa được restore. Hãy dừng lại và đọc lại output của psql.

Có một cảnh báo về hướng dẫn restore chính thức bằng CLI, bắt đầu bằng docker compose down -v. -v xóa named volume. Trong compose file mặc định, UPLOAD_LOCATION và DB_DATA_LOCATION là bind mount nên vẫn còn sau lệnh này. Nếu bạn đã đổi một trong hai thành named volume, command đó sẽ xóa ảnh của bạn. Hãy đọc compose file trước khi chạy command.

Vì sao timeline trống sau khi khôi phục

Timeline được tạo từ các row trong database. Immich không quét upload/ khi khởi động để tìm lại ảnh, vì file không có row sẽ không có owner, ngày tháng hoặc album. Vì vậy, lỗi khôi phục thường gặp nhất là file đã được khôi phục nhưng database bị thiếu. Immich khởi động, tạo schema trống và cung cấp cho bạn một instance hoạt động nhưng không có dữ liệu, trong khi ổ đĩa vẫn chứa đầy ảnh. Không có gì bị mất. Nhưng cũng không có gì hiển thị. Cách khắc phục là replay dump khi server đã dừng, đúng như phần trên.

Trường hợp thứ hai khó nhận biết hơn. Database được khôi phục, timeline hiển thị các entry, nhưng asset nào cũng không mở được. Điều đó có nghĩa là các row trỏ đến những file mà container không nhìn thấy. Nguyên nhân thường là library, upload và profile bị lồng sâu hơn một cấp sau khi có restic restore --target /restore nhưng không ai đưa nó về đúng vị trí. Hãy kiểm tra từ bên trong container thay vì đoán:

docker exec immich_server ls /data

File compose mặc định mount UPLOAD_LOCATION vào /data, vì vậy kết quả liệt kê phải hiển thị library, upload và profile. Nếu kết quả hiển thị một thư mục trống hoặc một thư mục srv thừa, bind mount của bạn đang trỏ sai cấp thư mục còn các row vẫn đúng.

Khớp version giữa backup và restore

Immich phát hành thường xuyên và schema thay đổi theo từng release, vì vậy dump chứa schema của server đã tạo ra nó.

Restore dump cũ vào server mới hơn thường hoạt động, vì server sẽ áp dụng các migration còn thiếu khi khởi động và đưa schema lên version mới. Tuy vậy, tài liệu backup của Immich vẫn khuyên bạn restore vào version tương thích khi có thể, vì không khớp version đồng nghĩa với việc migration sẽ chạy trong lúc restore. Project giữ các breaking change trong major release và ghi lại chúng trong release notes, vì vậy restore vượt qua major version là trường hợp bạn cần đọc kỹ trước tiên.

Restore dump mới hơn vào server cũ hơn hoàn toàn không hoạt động. Dump chứa các table và column mà code cũ không biết đến. Immich cũng nêu rõ rằng downgrade không được hỗ trợ, kể cả giữa các patch release. Không có rollback command nào để sử dụng trong trường hợp này.

Vì vậy, cách restore an toàn khá đơn giản. Chạy đúng version đã tạo dump, replay dump, đăng nhập, xác nhận timeline đầy đủ, rồi chỉ upgrade sau đó. Upgrade theo hướng dẫn trên trang upgrade chính thức: đọc release notes để kiểm tra breaking change, đặt IMMICH_VERSION trong .env thành release bạn muốn, rồi chạy docker compose pull && docker compose up -d. Giữ lại các dump trong một tuần cũng hữu ích: nếu bản mới nhất được tạo trong lúc upgrade thất bại, bản của hôm qua vẫn còn trong repository.

Xác minh backup mỗi tháng

Một backup mà bạn chưa từng restore chỉ là phỏng đoán. Mỗi tháng một lần, hãy restore backup vào một instance tạm thời rồi mở một bức ảnh để kiểm tra. Bài kiểm tra này mất khoảng hai mươi phút. Đây là việc duy nhất biến toàn bộ nội dung còn lại của trang này thành một recovery plan.

restic snapshots
restic stats latest

snapshots phải liệt kê lần chạy tối qua. stats latest phải báo kích thước gần với dung lượng thư viện của bạn, không phải chỉ vài megabyte.

Hãy restore vào một thư mục tạm, tốt nhất là trên một host dự phòng:

restic restore latest --target /tmp/immich-drill

Sao chép docker-compose.yml và .env ra khỏi bộ đã restore, rồi thay đổi ba thứ trong bản sao. Trỏ UPLOAD_LOCATION và DB_DATA_LOCATION đến các thư mục bên dưới /tmp/immich-drill. Publish web port ở một vị trí khác, dùng 12283:2283 thay vì 2283:2283. Xóa các dòng container_name:, vì file compose mặc định hard-code các tên như immich_server. Do đó, stack thứ hai trên cùng host sẽ bị trùng với stack thứ nhất và Docker từ chối tạo stack đó.

Chạy chuỗi restore ở phần trên: chỉ restore database, replay dump, rồi chạy docker compose up -d. Sau đó thực hiện 4 kiểm tra để xác nhận kết quả.

  1. Đăng nhập bằng password bạn đã dùng trước khi kiểm tra. Tài khoản hoạt động cho biết dump đã được restore.
  2. Mở timeline và cuộn đến tháng cũ nhất. Asset trải dài toàn bộ khoảng thời gian cho biết tất cả row đã được khôi phục, không chỉ các row gần đây.
  3. Mở một ảnh ở kích thước đầy đủ và tải original xuống.
  4. So sánh ảnh đó với cùng file trong thư viện đang chạy bằng sha256sum. Hash trùng nhau cho biết các byte đã qua được toàn bộ vòng round trip qua restic.

Sau đó dọn bài kiểm tra bằng docker compose down -v trong thư mục kiểm tra và xóa /tmp/immich-drill. Ghi ngày thực hiện ở nơi bạn sẽ nhìn thấy, vì giá trị của việc này hoàn toàn nằm ở việc lặp lại vào tháng sau. Nếu bạn vẫn đang cân nhắc nên chọn photo server nào, phần so sánh PhotoPrism và Immich giải thích hai hệ thống khác nhau như thế nào chính ở khía cạnh này.

FAQ

Tôi có phải dừng Immich để backup không?

Dừng immich_server và để immich_postgres tiếp tục chạy. Database không cần tạm dừng, vì pg_dump đọc trong một MVCC snapshot duy nhất và luôn thấy cùng một thời điểm nhất quán, bất kể tiến trình khác đang ghi gì. Cần dừng các file vì server vẫn ghi upload mới, còn storage template job vẫn di chuyển file giữa các thư mục. Do đó, backup tool có thể đọc một file khi file đó mới được ghi một phần và lưu bản sao bị cắt mà không báo lỗi. Chạy docker stop immich_server trước snapshot và docker start immich_server sau snapshot sẽ loại bỏ race này.

Tôi có thể copy thư mục dữ liệu Postgres thay vì chạy pg_dump không?

Không thể khi Postgres đang chạy. Việc copy lần lượt một data directory đang hoạt động sẽ đọc các file ở những thời điểm khác nhau. Kết quả không đại diện cho một trạng thái nhất quán, và Postgres sẽ từ chối khởi động với PANIC: could not locate a valid checkpoint record hoặc phát sinh lỗi sau đó khi gặp page bị hỏng. Bản copy đầy đủ được tạo khi Postgres đã dừng là một backup hợp lệ, nhưng chỉ mở được với cùng major version của Postgres và các extension tương thích. Immich cố định image Postgres 14 có VectorChord, pgvector và pgvecto.rs. Directory này sẽ không mở được với major version khác. SQL dump là văn bản thuần và có thể replay vào bất kỳ server tương thích nào, nên đây vẫn là bản copy cần giữ.

Tại sao timeline Immich trống sau khi restore?

Vì timeline được dựng từ các row trong database, còn bạn chỉ restore file mà không restore database. Immich không bao giờ quét upload/ để tìm lại ảnh, nên các file không có row tương ứng sẽ vẫn bị ẩn. Các ảnh vẫn còn nguyên. Dừng server, replay dump vào một Postgres vừa được khởi tạo, rồi khởi động stack. Nếu timeline vẫn đầy nhưng mọi ảnh đều không mở được, vấn đề là ngược lại: library, upload và profile không nằm trực tiếp trong directory được bind vào container. Kiểm tra bằng docker exec immich_server ls /data.

Có thể bỏ qua những thư mục Immich nào khi backup?

thumbs và encoded-video có thể được tạo lại từ file gốc, còn DB_DATA_LOCATION được dựng lại từ dump, nên không thư mục nào trong số đó bắt buộc phải có trong backup set. Bỏ qua chúng sẽ chuyển thời gian sang sau khi restore thay vì tốn storage trước khi restore, vì việc dựng lại preview và transcode cho một library lớn có thể cần hàng giờ CPU, chạy từ Administration > Jobs cho các asset bị thiếu. Bạn không bao giờ được bỏ qua library, upload và profile, vì chúng chứa bản sao duy nhất của từng file gốc.

Có thể restore dump Immich vào version mới hơn không?

Thông thường là có, vì server sẽ áp dụng các migration đang chờ khi khởi động và nâng schema theo từng bước. Chiều ngược lại sẽ thất bại: Immich không hỗ trợ downgrade, kể cả giữa các patch release, nên dump từ release mới hơn không thể được load vào server cũ hơn. Restore với IMMICH_VERSION được cố định ở release đã ghi dump, xác nhận timeline đầy đủ, rồi mới upgrade. Ghi release bên cạnh từng dump bằng curl -fsS http://localhost:2283/api/server/version, vì docker inspect chỉ echo image reference đã cấu hình, còn IMMICH_VERSION=v3 mặc định là floating tag và không cho biết thông tin cụ thể nào.

Bài viết này có hữu ích không?