SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor

Hướng dẫn tự host Zitadel trên VPS bằng Docker

Tự host Zitadel trên VPS cần tối thiểu 4 nhân CPU và 8GB RAM. Hướng dẫn này giúp bạn cấu hình Postgres, masterkey, TLS, SMTP, backup và xử lý database khi nâng cấp phiên bản.

Những yêu cầu để tự host Zitadel trên VPS

Để tự host Zitadel trên VPS, bạn cần một Docker host, một tên miền công khai trỏ về máy chủ đó, PostgreSQL, cùng khoảng 4 nhân CPU và 8 GB RAM. Zitadel là một identity provider. Nó cấp phát các token thông qua OIDC (OpenID Connect) và SAML (security assertion markup language) để các dịch vụ khác của bạn không cần phải tự quản lý danh sách người dùng riêng nữa. Việc cài đặt bao gồm một curl và một docker compose up. Các thành phần quyết định khả năng vận hành ổn định của hệ thống là masterkey, database user, SMTP (simple mail transfer protocol), bản backup và lần nâng cấp đầu tiên.

Mọi hướng dẫn dưới đây giả định bạn đang dùng Ubuntu 24.04, Docker Engine 24 trở lên với Compose plugin, và một tên miền như auth.example.com đã được trỏ về máy chủ.

Zitadel cần bao nhiêu tài nguyên VPS?

Hướng dẫn quickstart bằng Compose trong tài liệu của Zitadel yêu cầu 2 GB RAM. Con số đó chỉ dành cho máy tính cá nhân. Hướng dẫn triển khai production của Zitadel đưa ra các con số khác.

ChartZitadel's own published sizing guidance, August 2026
The data behind this chart
[
  {
    "config": "Process floor, no load",
    "cpu_cores": 0.5,
    "ram_gb": 0.5
  },
  {
    "config": "Single node, reduced setup",
    "cpu_cores": 4,
    "ram_gb": 8
  },
  {
    "config": "HA node, logs and metrics on",
    "cpu_cores": 4,
    "ram_gb": 16
  }
]

Đó là các khuyến nghị được công bố, không phải số liệu đo đạc từ một máy chủ đang chạy. Hãy xem chúng như định hướng quy mô. Bản thân tiến trình Zitadel khá nhẹ, chiếm khoảng 0.5 GB RAM khi ở trạng thái nghỉ. Số nhân CPU cần thiết cho việc băm mật khẩu (password hashing), vốn được thiết kế để chạy chậm, nên các đợt đăng nhập ồ ạt sẽ gây ra mức sử dụng CPU tăng đột biến. PostgreSQL chiếm phần còn lại của tài nguyên: hướng dẫn tương tự ước tính khoảng một nhân cho mỗi 100 yêu cầu mỗi giây và 4 GB RAM cho mỗi nhân. Kết hợp cả hai, bạn sẽ có 4 nhân và 8 GB như hướng dẫn đã nêu cho một node đơn lẻ, hoặc 16 GB mỗi node khi đã bật tính năng logging và metrics.

Vì vậy, một VPS 2 GB sẽ khởi động được stack này, nhưng nó thấp hơn mức khuyến nghị của dự án cho bất kỳ môi trường thực tế nào. Đăng nhập là dịch vụ mà mọi dịch vụ khác đều phụ thuộc vào. Khi nó ngừng hoạt động, không ứng dụng nào tin tưởng nó có thể cho phép người dùng truy cập. Việc quyết định rằng 8 GB là quá nhiều chi phí cho xác thực là một lựa chọn hợp lý, và việc đưa ra quyết định này bây giờ sẽ rẻ hơn nhiều so với sau khi đã migration. So sánh giữa Keycloak, Authentik và Zitadel đề cập đến chi phí bộ nhớ và công sức vận hành của từng loại, và tự host một server Authentik thường là giải pháp cho các máy chủ có cấu hình thấp hơn.

Lấy stack và ghim phiên bản

mkdir zitadel-compose && cd zitadel-compose
curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.yml
curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/.env.example
cp .env.example .env
chmod 600 .env

File đó định nghĩa bốn service mà bạn sẽ thực sự chạy. Traefik đóng vai trò là reverse proxy: nó định tuyến theo đường dẫn và, với overlay ở phía dưới, thực hiện TLS (transport layer security) termination. zitadel-api là file binary Go chạy trên cổng 8080. zitadel-login là giao diện đăng nhập được phục vụ tại /ui/v2/login. postgres chứa mọi thứ. Một Redis cache và một OpenTelemetry collector nằm trong cùng file đó đằng sau các Compose profile và sẽ không chạy cho đến khi bạn yêu cầu.

Đừng chạy docker compose up vội. Lần khởi động đầu tiên sẽ tạo instance, và một vài thiết lập bên dưới không thể thay đổi sau đó nếu không thực hiện thêm các bước phức tạp.

File .env mà bạn đã copy sẽ ghim các image tag của riêng nó:

ZITADEL_VERSION=v4.16.0
TRAEFIK_IMAGE=traefik:v3.7.7
POSTGRES_IMAGE=postgres:17.10-alpine

Bản release v4 hiện tại là v4.17.1, được công bố vào ngày 14 tháng 8 năm 2026. Hãy đặt ZITADEL_VERSION thành phiên bản bạn muốn chạy, và giữ ở nhánh v4 thay vì theo dõi bất kỳ bản nào mới nhất. File curl ở trên pull docker-compose.yml từ nhánh main, vốn không được ghim vào bất kỳ đâu, vì vậy hãy commit bản copy của cả hai file này vào một git repository. Nếu không, cùng một lệnh đó trên một máy chủ mới vào tháng sau sẽ cho bạn một file khác và bạn sẽ không biết những gì đã thay đổi.

Cấp cho Postgres một user riêng và mật khẩu thực

Bản phát hành .env kết nối Zitadel với PostgreSQL dưới quyền superuser, với mật khẩu là postgres:

POSTGRES_ADMIN_USER=postgres
POSTGRES_ADMIN_PASSWORD=postgres
ZITADEL_DATABASE_POSTGRES_DSN=postgresql://postgres:postgres@postgres:5432/zitadel?sslmode=disable

Có một cái bẫy trong bước bảo mật tại đây. Tài liệu của Zitadel hướng dẫn bạn thêm POSTGRES_ZITADEL_PASSWORD vào .env, nhưng docker-compose.yml mặc định không bao giờ đọc biến đó, nên việc thiết lập nó không có tác dụng gì. Việc tự ý thay đổi POSTGRES_ADMIN_PASSWORD sẽ làm hỏng kết nối, vì mật khẩu cũng được viết trực tiếp bên trong chuỗi DSN (data source name). DSN là dòng quyết định cách Zitadel kết nối.

Các chú thích trong .env.example giải thích rõ ràng: khi DSN được cấu hình, Zitadel sử dụng trực tiếp user đó và không tự tạo một user không có đặc quyền cho bạn, vì vậy role phải tồn tại trước lần khởi động đầu tiên. Hãy tạo mật khẩu, khởi động Postgres riêng, và tạo role.

tr -dc A-Za-z0-9 </dev/urandom | head -c 32; echo

docker compose --env-file .env -f docker-compose.yml up -d postgres

docker compose --env-file .env -f docker-compose.yml exec -T postgres \
  psql -U postgres -d postgres <<'SQL'
CREATE ROLE zitadel LOGIN PASSWORD 'the-password-you-generated';
ALTER DATABASE zitadel OWNER TO zitadel;
SQL

docker compose --env-file .env -f docker-compose.yml exec -T postgres \
  psql -U postgres -d zitadel -c 'ALTER SCHEMA public OWNER TO zitadel;'

Các lệnh psql đó chạy bên trong container thông qua socket cục bộ, vốn được image Postgres chính thức tin tưởng, nên chúng không yêu cầu nhập mật khẩu. Quyền sở hữu là phần quan trọng. Trên PostgreSQL 15 trở lên, một GRANT ALL PRIVILEGES ON DATABASE thông thường không còn cho phép role tạo bảng trong schema public, nên giai đoạn thiết lập của Zitadel sẽ thất bại với lỗi phân quyền khi xây dựng các schema. Việc cấp quyền sở hữu database và schema cho role sẽ tránh được lỗi này.

Bây giờ hãy trỏ DSN vào role mới, và thiết lập mật khẩu admin thực trong khi bạn đang ở trong file:

POSTGRES_ADMIN_PASSWORD=a-32-character-random-string
ZITADEL_DATABASE_POSTGRES_DSN=postgresql://zitadel:the-password-you-generated@postgres:5432/zitadel?sslmode=disable

sslmode=disable là ổn trong trường hợp này vì Postgres chỉ có thể truy cập trên mạng Compose nội bộ và cổng của nó không bao giờ được publish ra host. Sau lần khởi động hoàn chỉnh đầu tiên, hãy kiểm tra xem role đó có thực sự sở hữu dữ liệu của nó không:

docker compose exec -T postgres psql -U zitadel -d zitadel -c '\dn'

Kết quả sẽ liệt kê schema eventstore và schema projections. Danh sách trống nghĩa là giai đoạn thiết lập chưa bao giờ chạy đến đó, và log của container API sẽ cho biết lý do.

Masterkey và cái giá khi làm mất nó

Zitadel mã hóa các secret trước khi lưu trữ: client secret, thông tin xác thực của identity provider, mật khẩu SMTP, seed cho one-time-password và machine key. Masterkey dùng để giải mã tất cả những thứ đó. Nó dài đúng 32 ký tự và tài liệu đã nêu rõ hậu quả: bạn không thể thay đổi nó mà không làm mất quyền truy cập vào dữ liệu đã mã hóa.

Hãy tạo một key và thay thế dòng placeholder trong .env:

tr -dc A-Za-z0-9 </dev/urandom | head -c 32; echo

Hãy sửa dòng ZITADEL_MASTERKEY=MasterkeyNeedsToHave32Characters thay vì thêm một dòng thứ hai. Docker Compose lấy định nghĩa cuối cùng của một key bị lặp, nên việc thêm mới vẫn hoạt động, nhưng một file chứa hai dòng masterkey là cái bẫy cho bất kỳ ai đọc nó sau này.

Bây giờ hãy xem xét nơi lưu trữ key đó. File compose khởi chạy container API như sau:

command: start-from-init --masterkey "${ZITADEL_MASTERKEY}"

Do đó, masterkey nằm trên dòng lệnh của container, nơi docker inspect hiển thị nó cho bất kỳ ai có quyền truy cập vào Docker socket. Trên một VPS chỉ có một admin, đây là sự đánh đổi chấp nhận được và chế độ trên .env là thứ bảo vệ nó trên ổ đĩa. Nếu điều này không chấp nhận được, hãy mount key dưới dạng một file và sử dụng --masterkeyFile /run/secrets/zitadel-masterkey thay thế, cách này giúp giá trị không bị lộ trong các đối số của tiến trình.

Hãy sao chép masterkey vào trình quản lý mật khẩu của bạn trước lần khởi động đầu tiên. Nó không xuất hiện trong bản dump cơ sở dữ liệu, vì vậy một bản dump được khôi phục dưới một masterkey khác sẽ tạo ra một instance không thể đọc được các secret của chính nó. Hãy giữ nó ở một nơi khác với kho lưu trữ chứa bản dump, để nếu một bản backup bị đánh cắp thì nó cũng không chứa cả dữ liệu đã mã hóa lẫn key để giải mã.

Thiết lập domain bên ngoài trước lần khởi động đầu tiên

ZITADEL_DOMAIN trong .env cung cấp dữ liệu cho ZITADEL_EXTERNALDOMAIN bên trong container, và đây chính là tên mà người dùng của bạn sẽ nhập vào. Zitadel dựa vào đó để xác định OIDC issuer, URI cơ sở của giao diện đăng nhập, các SAML endpoint và tên đăng nhập của quản trị viên đầu tiên, vì vậy đây không phải là thông tin chỉ để hiển thị.

ZITADEL_DOMAIN=auth.example.com
ZITADEL_EXTERNALPORT=443
ZITADEL_EXTERNALSECURE=true

Zitadel xác định instance mà bạn đang truy cập dựa trên header Host. Nếu header đó không khớp với một domain mà nó nhận diện, mọi yêu cầu đều nhận được cùng một phản hồi:

ID=QUERY-1kIjX Message=Instance not found

Đây là lỗi phổ biến nhất khi tự host Zitadel, và nó gần như luôn xuất phát từ một trong hai nguyên nhân. Hoặc là ZITADEL_DOMAIN không phải là tên mà bạn đang truy cập, hoặc một proxy phía trước đang ghi đè Host thành địa chỉ upstream. Việc truy cập bằng địa chỉ IP của máy chủ thay vì tên domain cũng gây ra lỗi này.

Bạn có thể thay đổi các giá trị này sau. Zitadel cần chạy lại giai đoạn setup để áp dụng thay đổi, và mọi ứng dụng bạn đã đăng ký trước đó vẫn sẽ giữ nguyên các redirect URI cũ. Việc chọn tên miền cuối cùng ngay từ đầu sẽ tiết kiệm công sức hơn nhiều so với việc di chuyển nó sau này.

Kết thúc TLS với overlay Let's Encrypt

Đối với một domain công khai, hãy thêm overlay Let's Encrypt của Zitadel. Nó chuyển Traefik sang cơ chế ACME (automatic certificate management environment) HTTP challenge và thay thế các cổng được publish bằng 80 và 443, vì vậy không dịch vụ nào khác trên máy chủ được phép chiếm giữ các cổng này.

curl -fsSLO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.mode-letsencrypt.yml
echo 'LETSENCRYPT_EMAIL=ops@example.com' >> .env

Overlay này cũng thiết lập ZITADEL_EXTERNALPORT: 443ZITADEL_EXTERNALSECURE: true trên container API, đây là lý do tại sao URL công khai và các URL mà Zitadel tự tạo ra khớp với nhau. Bản ghi A phải được phân giải trước khi bạn bắt đầu, vì HTTP challenge sẽ thất bại nếu không có nó.

Nếu bạn đã kết thúc TLS trên nginx hoặc trên một load balancer, hãy sử dụng docker-compose.mode-external-tls.yml thay thế và thiết lập TRAEFIK_TRUSTED_IPS thành các dải IP mà proxy của bạn gửi lưu lượng đến. Traefik chỉ chấp nhận các header X-Forwarded-* từ các địa chỉ nằm trong danh sách đó, vì vậy một giá trị sai sẽ khiến giao thức được chuyển tiếp bị loại bỏ và Zitadel bắt đầu tạo ra các URL http:// cho một trang web HTTPS.

Một upstream proxy có hai nhiệm vụ mà Zitadel yêu cầu nghiêm ngặt. Nó phải giao tiếp HTTP/2 với backend, vì API là gRPC. Và nó phải chuyển tiếp Host mà không thay đổi, cùng với X-Forwarded-Proto: https. Ví dụ về nginx của chính Zitadel cho thấy cấu trúc này:

server {
    listen 443 ssl;
    http2 on;
    ssl_certificate     /etc/certs/selfsigned.crt;
    ssl_certificate_key /etc/certs/selfsigned.key;
    location /ui/v2/login {
        proxy_pass http://login-external-tls:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto https;
    }
    location / {
        grpc_pass grpc://zitadel-external-tls:8080;
        grpc_set_header Host $host;
        grpc_set_header X-Forwarded-Proto https;
    }
}

Các tên upstream ở đó là các container trong thiết lập thử nghiệm của Zitadel, vì vậy hãy thay thế chúng bằng tên của bạn. Nếu bạn phục vụ Zitadel trên một cổng khác ngoài 443, hãy sử dụng grpc_set_header Host $host:$server_port; để cổng được truyền đi cùng với header. Phần còn lại là một virtual host thông thường, và cấu hình nginx reverse proxy được giải thích từng dòng bao gồm các phần không dành riêng cho Zitadel.

Tài khoản quản trị đầu tiên và bắt buộc đổi mật khẩu

Lần khởi động đầu tiên sẽ tạo ra một instance, một tổ chức và một quản trị viên. Tên đăng nhập là zitadel-admin@ cộng với zitadel. cộng với domain bên ngoài của bạn, vì vậy với ZITADEL_DOMAIN=auth.example.com, tên đăng nhập sẽ là:

zitadel-admin@zitadel.auth.example.com

Mật khẩu mặc định là Password1! trừ khi bạn tự thiết lập mật khẩu riêng. Mặc định của Zitadel là bắt buộc đổi mật khẩu ở lần đăng nhập đầu tiên, và file compose được cung cấp đã ghi đè mặc định đó:

ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: false

Dòng đó được hardcode trong docker-compose.yml thay vì đọc từ .env, vì vậy hãy đặt các giá trị của riêng bạn vào một file overlay. Hãy gọi nó là docker-compose.local.yml:

services:
  zitadel-api:
    environment:
      ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS: you@example.com
      ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORD: "a-long-temporary-password"
      ZITADEL_FIRSTINSTANCE_ORG_HUMAN_PASSWORDCHANGEREQUIRED: "true"

Compose chỉ tự động tải docker-compose.override.yml khi bạn chạy mà không có flag -f, và mọi lệnh trong hướng dẫn của Zitadel đều truyền vào -f, điều này sẽ vô hiệu hóa tính năng đó. Thay vì lặp lại danh sách các flag ngày càng dài, hãy ghim danh sách file trong .env:

COMPOSE_FILE=docker-compose.yml:docker-compose.mode-letsencrypt.yml:docker-compose.local.yml

Bây giờ hãy khởi động nó:

docker compose pull
docker compose up -d --wait

--wait sẽ giữ lệnh cho đến khi các healthcheck vượt qua. Khi container API không bao giờ đạt trạng thái đó, Compose sẽ dừng lại với dependency failed to start: container zitadel-compose-zitadel-api-1 is unhealthy, và docker compose logs zitadel-api sẽ chứa nguyên nhân. Trong lần khởi động đầu tiên, nguyên nhân thường là độ dài của masterkey hoặc DSN của database.

Hãy đăng nhập tại https://auth.example.com/ui/console, đổi mật khẩu, sau đó bật xác thực hai yếu tố cho tài khoản đó trước khi tạo bất kỳ thứ gì khác. Mọi giá trị ZITADEL_FIRSTINSTANCE_* chỉ áp dụng trong lúc instance đầu tiên được tạo. Một khi instance đã tồn tại, việc chỉnh sửa chúng sẽ không có tác dụng gì cả.

Tại sao việc đặt lại mật khẩu không có tác dụng cho đến khi SMTP hoạt động

Một identity provider không thể gửi mail là một hệ thống bị lỗi theo cách khó phát hiện trong nhiều tuần. Zitadel gửi email cho các mục đích mời người dùng, xác thực địa chỉ, liên kết đặt lại mật khẩu, mã dùng một lần và thông báo xác nhận quyền sở hữu tên miền. Nếu không cấu hình SMTP provider, Console vẫn báo cáo hành động đã hoàn tất, và tin nhắn được chuyển đến một notification worker mà không có nơi nào để gửi đi. Các thiết lập mặc định cung cấp cho worker đó MaxAttempts: 3MaxTtl: 5m, vì vậy nó sẽ thử lại vài lần trong vài phút rồi dừng lại. Không có thông báo nào gửi đến người đang chờ liên kết đó.

Hãy cấu hình trong Console, tại phần cài đặt instance ở https://auth.example.com/ui/console/settings. Biểu mẫu SMTP provider yêu cầu địa chỉ email người gửi, tên người gửi, host và port, user, mật khẩu SMTP và tùy chọn bật/tắt TLS. Hãy sử dụng nút kiểm tra (test) trong biểu mẫu đó trước khi lưu, vì nó sẽ gửi một tin nhắn thực tế: tin nhắn sẽ đến hoặc không.

Có một bộ biến môi trường tương ứng, ZITADEL_DEFAULTINSTANCE_SMTPCONFIGURATION_SMTP_HOST và các biến liên quan. Chúng áp dụng khi một instance được tạo. Trên một stack đang chạy, chúng không có tác dụng, vì vậy Console là nơi phù hợp nhất cho một instance đã tồn tại.

Có hai điều cần lưu ý về việc gửi mail từ VPS, vì đây là nơi thường xảy ra lỗi. Hầu hết các nhà cung cấp chặn cổng 25 chiều ra đối với tài khoản mới, vì vậy việc gửi trực tiếp đến máy chủ mail của người nhận sẽ bị timeout mà không có lỗi cụ thể nào. Hãy sử dụng một authenticated relay trên cổng 587 thay thế. Đồng thời, hãy công bố các bản ghi SPF (sender policy framework) và DKIM (domainkeys identified mail) cho tên miền gửi, nếu không liên kết đặt lại mật khẩu sẽ rơi vào mục spam, điều mà người dùng sẽ thấy giống như mail không bao giờ được gửi đi.

Hãy kiểm tra trước khi mời bất kỳ ai. Tạo một người dùng dùng thử, yêu cầu đặt lại mật khẩu và theo dõi xem tin nhắn có đến không. Nếu không, docker compose logs -f zitadel-api sẽ chỉ ra lỗi SMTP. Mật khẩu SMTP được lưu trữ dưới dạng mã hóa trong database, đây là một thứ khác mà masterkey đang bảo vệ cho bạn.

Sao lưu riêng biệt Postgres và masterkey

Mọi dữ liệu mà Zitadel quản lý đều nằm trong PostgreSQL. Thành phần giải mã dữ liệu đó là masterkey. Hãy sao lưu chúng vào hai vị trí khác nhau.

Đầu tiên là dump dữ liệu:

sudo install -d -m 700 /srv/zitadel-backups
docker compose exec -T postgres \
  pg_dump -U postgres -Fc zitadel > "/srv/zitadel-backups/zitadel-$(date +%F).dump"

-Fc là định dạng tùy chỉnh, giúp nén dữ liệu ngay khi xuất ra và pg_restore có thể đọc chọn lọc. exec -T loại bỏ terminal, điều này quan trọng vì lệnh này chạy từ cron mà không có terminal đính kèm.

Sau đó, đẩy thư mục đó ra ngoài site bằng restic, công cụ này sẽ mã hóa và khử trùng lặp dữ liệu:

export RESTIC_REPOSITORY="sftp:backup@backup.example.com:/srv/restic/zitadel"
export RESTIC_PASSWORD_FILE=/root/.restic-password
restic init
restic backup /srv/zitadel-backups
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune

restic init chỉ chạy một lần vào ngày đầu tiên. Đặt lệnh dump và hai lệnh cuối vào /usr/local/bin/zitadel-backup.sh và chạy hàng đêm:

0 3 * * * /usr/local/bin/zitadel-backup.sh

Sao lưu .env và mọi file compose bạn sử dụng vào git. Masterkey là ngoại lệ đối với tất cả các quy trình này. Nó phải nằm trong trình quản lý mật khẩu của bạn và tại một vị trí thứ hai không phải là repository restic này, vì một kho lưu trữ chứa cả cơ sở dữ liệu và khóa giải mã của nó sẽ không còn là bản sao lưu của một hệ thống đã mã hóa nữa.

Một bản sao lưu chưa được khôi phục chỉ là một sự phỏng đoán. Hãy khôi phục vào một cơ sở dữ liệu tạm trên cùng máy chủ và kiểm tra nó:

docker compose exec -T postgres createdb -U postgres zitadel_restore_test
docker compose exec -T postgres pg_restore -U postgres -d zitadel_restore_test \
  < /srv/zitadel-backups/zitadel-2026-08-21.dump
docker compose exec -T postgres psql -U postgres -d zitadel_restore_test -c '\dt eventstore.*'
docker compose exec -T postgres dropdb -U postgres zitadel_restore_test

Danh sách các bảng trong schema eventstore cho thấy bản dump hợp lệ. Lỗi thông báo schema không tồn tại nghĩa là bản dump không hợp lệ, và bạn đã phát hiện ra điều đó vào một ngày mà bạn không bị thiệt hại gì. Mô hình chung để sao lưu và nâng cấp một Compose stack áp dụng ở đây gần như không thay đổi, và việc giữ masterkey nằm ngoài kho lưu trữ là phần duy nhất dành riêng cho Zitadel.

Nâng cấp Zitadel mà không làm mất instance

Việc nâng cấp là thay đổi phiên bản trong .env, theo sau là hai lệnh:

docker compose pull
docker compose up -d --wait

Hãy hiểu rõ lệnh thứ hai làm gì trước khi chạy nó trên hệ thống mà người dùng đang đăng nhập. Lệnh của container là start-from-init, nó thực hiện các giai đoạn init và setup trước khi bắt đầu phục vụ, và giai đoạn setup chính là thực hiện các migration cơ sở dữ liệu. Vì vậy, việc thay đổi phiên bản sẽ chạy schema migration trực tiếp trên cơ sở dữ liệu đang hoạt động của bạn khi container khởi động mà không cần giám sát, trong khi --wait sẽ đợi healthcheck. Đó là lý do tại sao bài kiểm tra restore ở trên là bắt buộc.

Hãy thực hiện một bản dump mới ngay trước khi nâng cấp. Bản dump của đêm qua là một việc khác.

Đừng nhảy vọt phiên bản major. Việc chuyển từ v3 lên v4 yêu cầu phải ở phiên bản v3.4.1 hoặc mới hơn trước, vì v4 đã loại bỏ các OIDC signing key cũ, do đó các token được ký bằng key cũ sẽ ngừng xác thực ngay khi bạn nâng cấp. Thông báo kỹ thuật A-10017 của Zitadel mô tả vấn đề này, và cách khắc phục là chạy phiên bản v3 mới hơn đủ lâu để các token cũ hết hạn trước khi bạn nâng cấp.

Theo dõi giai đoạn setup bằng docker compose logs -f zitadel-api. Các migration trên một eventstore lớn có thể mất vài phút, và Traefik sẽ không định tuyến đến API cho đến khi healthcheck vượt qua, vì vậy trang web sẽ ngừng hoạt động trong khoảng thời gian đó. Hãy lập kế hoạch thay vì để nó xảy ra bất ngờ.

Rollback không đơn giản là quay lại tag cũ. Khi các migration đã chạy, binary cũ hơn sẽ không hiểu được schema mà nó tìm thấy, vì vậy rollback đồng nghĩa với việc restore từ bản dump. Khi instance đã có người dùng thực tế, hãy chuyển sang docker-compose.prodlike.yml, đây là overlay chạy init và setup như các bước riêng biệt với quá trình khởi động, nhờ đó migration trở thành một thao tác bạn chủ động kích hoạt và theo dõi thay vì là tác dụng phụ của việc khởi động lại container.

Cấu hình trỏ đến identity provider mới của bạn

Trong Console, hãy tạo một project và sau đó là một application bên trong đó. Hãy chọn OIDC cho bất kỳ ứng dụng hiện đại nào, Zitadel sẽ cung cấp cho bạn một client ID, một client secret và một discovery document tại https://auth.example.com/.well-known/openid-configuration. Hầu hết các phần mềm tự host hỗ trợ single sign-on đều yêu cầu chính xác các thông tin này.

Nhiều phần mềm không hỗ trợ tính năng này, hoặc chỉ hỗ trợ trong các gói trả phí. Đối với trường hợp đầu tiên, oauth2-proxy đặt trước ứng dụng sẽ biến bất kỳ dịch vụ HTTP nào thành một dịch vụ được Zitadel bảo vệ. Đối với trường hợp thứ hai, bạn nên đọc bài thuế SSO trong các ứng dụng tự host trước khi lên kế hoạch di chuyển hệ thống dựa trên một tính năng mà bạn chưa trả phí.

FAQ

Zitadel tự host cần bao nhiêu RAM và CPU?

Hướng dẫn triển khai production của Zitadel khuyến nghị khoảng 4 nhân CPU và 8 GB RAM cho một node chạy cấu hình tối giản, và 16 GB mỗi node nếu có bật logging và metrics. PostgreSQL được tính toán riêng, ước tính khoảng một nhân cho mỗi 100 request mỗi giây và 4 GB RAM cho mỗi nhân. Bản Compose quickstart có thể khởi chạy trong 2 GB, đủ để dùng thử nhưng thấp hơn mức dự phòng mà dự án khuyến nghị cho một hệ thống có các dịch vụ khác phụ thuộc vào.

Chuyện gì xảy ra nếu tôi làm mất masterkey của Zitadel?

Mọi dữ liệu được mã hóa bằng key đó sẽ không thể giải mã. Các client secret, thông tin xác thực của identity provider, mật khẩu SMTP và các seed cho one-time-password sẽ không thể khôi phục, và key này không thể thay đổi sau khi đã thiết lập. Một bản dump database đơn thuần không thể khôi phục một instance đang hoạt động, vì bản dump chỉ chứa ciphertext mà không có key. Hãy lưu masterkey trong trình quản lý mật khẩu, tại một nơi tách biệt với bản backup chứa file dump. Nếu mất cả hai, cách duy nhất là xây dựng lại instance từ đầu.

Tại sao email đặt lại mật khẩu của Zitadel không bao giờ đến?

Vì bạn chưa cấu hình nhà cung cấp SMTP, hoặc cấu hình hiện tại không gửi được mail. Zitadel mặc định xếp hàng mỗi thông báo vào một worker với ba lần thử và vẫn báo thành công trong Console, nên lỗi này diễn ra âm thầm. Hãy cấu hình nhà cung cấp SMTP trong phần cài đặt instance và dùng nút kiểm tra (test) trong biểu mẫu đó để gửi một tin nhắn thực tế. Từ một VPS, hãy dùng relay có xác thực qua cổng 587, vì hầu hết các nhà cung cấp đều chặn cổng 25 chiều ra. Đồng thời, hãy publish bản ghi SPF và DKIM cho domain gửi mail để tránh bị lọc vào mục spam.

Tôi có thể đổi domain bên ngoài của Zitadel sau khi cài đặt không?

Có, nhưng không chỉ bằng cách chỉnh sửa .env. Hãy thay đổi ZITADEL_EXTERNALDOMAIN, ZITADEL_EXTERNALPORTZITADEL_EXTERNALSECURE, sau đó để Zitadel chạy lại giai đoạn setup để nó nhận cấu hình mới. Các ứng dụng bạn đã đăng ký trước đó vẫn giữ nguyên redirect URI cũ và phải được cập nhật thủ công. Mọi request có header Host không khớp với domain mà Zitadel biết sẽ nhận lại phản hồi Instance not found. Việc chọn tên miền cuối cùng trước lần khởi động đầu tiên sẽ giúp bạn tránh được tất cả các bước này.