Vũ Văn HảiFull-stack · AI-native
Hướng dẫnBlog
Trao đổi dự án

© 2026 Vũ Văn Hải · Viết từ kinh nghiệm triển khai thật.

Trang chủHướng dẫnBlogRSS
  1. Hướng dẫn
  2. /Database
  3. /Sửa lỗi Docker container không kết nối được PostgreSQL trên VPS

Sửa lỗi Docker container không kết nối được PostgreSQL trên VPS

Docker đổi dải IP mạng mỗi lần docker-compose down rồi up, nên PostgreSQL trên host từ chối container. Cách chẩn đoán subnet, mở UFW và pg_hba.conf, rồi cho phép cả dải 172.16.0.0/12 để sửa một lần là xong.

Cập nhật: 21 thg 9, 20266 phút đọc
DockerPostgreSQLMạngUFWVPS
Mục lục
  • 1. Tại sao Docker lại đổi IP?
  • 2. Chẩn đoán: tìm IP và tên mạng hiện tại
  • 3. Xem dải mạng (subnet) và gateway
  • 4. Mở firewall (UFW)
  • 5. Cập nhật cấu hình PostgreSQL
  • 5.1. Sửa pg_hba.conf
  • 5.2. Kiểm tra postgresql.conf
  • 5.3. Restart PostgreSQL
  • 6. Test lại
  • 7. Giải pháp Silver Bullet (khuyến nghị)
  • Cơ sở lý thuyết
  • Bước 1: Sửa pg_hba.conf
  • Bước 2: Mở firewall UFW
  • Bước 3: Restart PostgreSQL
  • Bước 4: Cập nhật connection string trong code
  • Tóm tắt nhanh
  • Lưu ý

Container đang chạy ngon bỗng không kết nối được PostgreSQL cài trên host? Nguyên nhân thường gặp: Docker tự động đổi dải IP mạng (dynamic allocation) mỗi khi bạn docker-compose down rồi up lại, khiến PostgreSQL từ chối kết nối vì IP mới không nằm trong danh sách được phép. Bài này chỉ cách chẩn đoán, sửa nhanh cho subnet hiện tại, và giải pháp làm một lần cho mọi subnet. Áp dụng cho PostgreSQL 14/16 trên VPS Ubuntu/Debian chạy Docker.

Trong bài, myapp là tên container và myapp_default là tên mạng Docker ví dụ - thay bằng tên thật của bạn. Các đường dẫn dùng PostgreSQL 16, đổi 16 theo phiên bản của bạn.

1. Tại sao Docker lại đổi IP?

Hãy tưởng tượng Docker quản lý mạng giống như việc xếp phòng trong khách sạn:

  • Lần đầu chạy: bạn chạy docker-compose up cho dự án myapp. Docker thấy "Phòng 1" (172.17.x.x) đang trống nên cấp cho dự án của bạn.
  • Lần sau:
    1. Bạn cài thêm một dự án khác (ví dụ: my-blog). Dự án này chiếm "Phòng 1" (172.17.x.x).
    2. Sau đó bạn chạy lại dự án myapp (docker-compose up).
    3. Docker thấy "Phòng 1" đã có người ở nên tự động đẩy dự án sang "Phòng 2" (172.18.x.x) hoặc "Phòng 3" (172.19.x.x).

Hậu quả: lúc trước bạn cấu hình PostgreSQL chỉ cho phép "Phòng 1" (172.17.0.1) truy cập. Giờ dự án bị đẩy sang "Phòng 2" (172.18.0.1), PostgreSQL thấy IP lạ và chặn ngay lập tức - lỗi kết nối xuất hiện.

2. Chẩn đoán: tìm IP và tên mạng hiện tại

Chạy lệnh sau để xem container đang nhận IP nào và thuộc mạng nào (thay myapp bằng tên container của bạn):

# Nếu đã cài jq (đẹp hơn)
docker inspect -f '{{json .NetworkSettings.Networks}}' myapp | jq .

# Nếu chưa cài jq (dùng grep)
docker inspect myapp | grep -A 20 "Networks"

Bạn cần tìm hai thông tin:

Thông tinVí dụ
Network Namemyapp_default
IPAddress172.19.0.2

3. Xem dải mạng (subnet) và gateway

Sau khi có tên mạng ở bước 2, chạy lệnh sau để xem cấu trúc của mạng đó:

# Thay tên mạng bằng tên bạn tìm được ở bước 2
docker network inspect myapp_default

Kết quả trả về sẽ có đoạn Config như này:

"Config": [
    {
        "Subnet": "172.18.0.0/16",
        "Gateway": "172.18.0.1"
    }
]

Gateway chính là địa chỉ IP mà container dùng để kết nối ra ngoài, bao gồm cả kết nối tới PostgreSQL trên host.

4. Mở firewall (UFW)

Chạy lệnh này trên VPS để UFW cho phép dải mạng Docker đi qua cổng PostgreSQL (5432):

# Thay 172.21.0.0/16 bằng subnet bạn tìm được ở bước 3
sudo ufw allow from 172.21.0.0/16 to any port 5432
sudo ufw reload

5. Cập nhật cấu hình PostgreSQL

5.1. Sửa pg_hba.conf

# Tìm file config (thường ở /etc/postgresql/14/main/ hoặc 16/main/)
sudo nano /etc/postgresql/16/main/pg_hba.conf

Thêm dòng này vào cuối file (thay subnet cho phù hợp):

# Cho phép Docker container kết nối tới PostgreSQL
host    all             all             172.21.0.0/16           scram-sha-256

Nếu bạn dùng xác thực md5 thì đổi scram-sha-256 thành md5. Kiểm tra trong log lỗi để biết bạn đang dùng loại nào.

5.2. Kiểm tra postgresql.conf

sudo nano /etc/postgresql/16/main/postgresql.conf

Tìm dòng listen_addresses:

  • Nếu đã để listen_addresses = '*' thì không cần sửa gì.
  • Nếu đang liệt kê từng IP (cách làm trong bài Cài đặt PostgreSQL trên VPS an toàn) thì bắt buộc phải thêm gateway của Docker vào danh sách (ví dụ: ,172.21.0.1).

5.3. Restart PostgreSQL

sudo systemctl restart postgresql

6. Test lại

Sau khi restart PostgreSQL, restart lại container API:

docker restart myapp

Kiểm tra log để xác nhận kết nối thành công:

docker logs myapp --tail 50

7. Giải pháp Silver Bullet (khuyến nghị)

Vấn đề với cách sửa ở trên: mỗi lần docker-compose down rồi up lại, Docker có thể đổi sang dải mạng khác (ví dụ 172.22.x.x). Bạn sẽ phải lặp lại toàn bộ các bước trên.

Giải pháp tối ưu: mở quyền truy cập cho toàn bộ dải IP mà Docker có thể sử dụng, làm một lần duy nhất.

Cơ sở lý thuyết

Theo chuẩn mạng RFC 1918, Docker được phép sử dụng dải IP private class B:

  • Từ 172.16.0.0 đến 172.31.255.255
  • Viết gọn theo CIDR: 172.16.0.0/12

Khi cấu hình cho phép 172.16.0.0/12:

Docker cấp IPĐược phép?
172.17.0.1Có (nằm trong dải)
172.21.0.1Có (nằm trong dải)
172.30.0.1Có (nằm trong dải)

Bước 1: Sửa pg_hba.conf

sudo nano /etc/postgresql/16/main/pg_hba.conf

Thêm dòng sau (hoặc thay thế dòng cũ):

# Silver Bullet: cho phép bất kỳ IP nào thuộc dải Docker (172.16.x.x -> 172.31.x.x)
host    all             all             172.16.0.0/12           scram-sha-256

Bước 2: Mở firewall UFW

sudo ufw allow from 172.16.0.0/12 to any port 5432
sudo ufw reload

Bước 3: Restart PostgreSQL

sudo systemctl restart postgresql

Bước 4: Cập nhật connection string trong code

Connection string vẫn phải dùng đúng IP gateway của mạng Docker hiện tại. Gateway luôn là .1 của subnet:

Subnet DockerHost trong connection string
172.18.x.x172.18.0.1
172.21.x.x172.21.0.1

Để không bao giờ phải sửa connection string, bạn có thể dùng host.docker.internal (trên Mac/Windows) hoặc cấu hình extra_hosts trong docker-compose.yml để map một tên cố định vào gateway. Tuy nhiên trên Linux VPS, dùng IP gateway trực tiếp là cách đơn giản và hiệu quả nhất.

Tóm tắt nhanh

# 1. Tìm tên mạng và IP của container
docker inspect myapp | grep -A 20 "Networks"

# 2. Xem subnet và gateway
docker network inspect <tên-mạng>

# 3. Mở firewall (Silver Bullet - làm 1 lần duy nhất)
sudo ufw allow from 172.16.0.0/12 to any port 5432
sudo ufw reload

# 4. Sửa pg_hba.conf - thêm dòng:
# host    all    all    172.16.0.0/12    scram-sha-256

# 5. Restart PostgreSQL
sudo systemctl restart postgresql

# 6. Restart container
docker restart myapp

Lưu ý

Triệu chứngCách xử lý
Log báo no pg_hba.conf entry for host "172.x.x.x"Subnet của container chưa có trong pg_hba.conf - thêm rule cho subnet đó (hoặc 172.16.0.0/12) rồi restart PostgreSQL
Rule đã đúng subnet nhưng vẫn lỗi xác thựcKiểm tra phương thức xác thực trong log lỗi: đổi scram-sha-256 thành md5 nếu server đang dùng md5
Container bị timeout khi kết nối tới port 5432UFW chưa mở port 5432 cho subnet Docker - làm lại bước 4
Container báo Connection refusedlisten_addresses đang liệt kê từng IP mà thiếu gateway của Docker - xem bước 5.2
Đang chạy ổn, sau docker-compose down rồi up thì lỗi trở lạiDocker đã cấp subnet mới - áp dụng giải pháp 172.16.0.0/12 và cập nhật IP gateway trong connection string
Bài trướcMẹo DBeaver: phóng to icon toolbar và sửa lỗi TimeZoneBài tiếp theoTối ưu hiệu suất VPS: swap, PostgreSQL và DB pool

Bài liên quan

  • Kết nối PostgreSQL trên VPS qua SSH Tunnel từ máy local

    Dùng SSH Tunnel để máy dev kết nối PostgreSQL trên VPS mà không phải mở port 5432 ra internet, kèm cách cấu hình DATABASE_URL cho môi trường local và production.

    Database

    Database
  • Cài Caddy làm reverse proxy trên VPS bằng Docker

    Dựng Caddy làm reverse proxy bằng Docker trên VPS mới: tạo network dùng chung, viết Caddyfile và docker-compose, tự động có HTTPS, rồi thêm domain mới chỉ bằng một lệnh reload không downtime.

    VPS

    VPS
  • Migrate database PostgreSQL giữa hai VPS bằng pg_dump và psql

    Di chuyển toàn bộ một database PostgreSQL (schema lẫn data) từ VPS nguồn sang VPS đích qua máy local: dump bằng pg_dump, restore trong một transaction, verify số dòng, cutover và rollback an toàn.

    Database

    Database

Viết bởi Vũ Văn Hải

Tôi là Hải, full-stack developer ở TP. Hồ Chí Minh. Các bài ở đây đúc kết từ những hệ thống tôi tự dựng và vận hành. Cần dựng hoặc gỡ rối một hệ thống tương tự? Cứ nhắn tôi.

Trao đổi dự ánXem thêm hướng dẫn

Thấy sai sót hoặc lệnh không còn chạy? Báo cho tôi

Mục lục

  • 1. Tại sao Docker lại đổi IP?
  • 2. Chẩn đoán: tìm IP và tên mạng hiện tại
  • 3. Xem dải mạng (subnet) và gateway
  • 4. Mở firewall (UFW)
  • 5. Cập nhật cấu hình PostgreSQL
  • 5.1. Sửa pg_hba.conf
  • 5.2. Kiểm tra postgresql.conf
  • 5.3. Restart PostgreSQL
  • 6. Test lại
  • 7. Giải pháp Silver Bullet (khuyến nghị)
  • Cơ sở lý thuyết
  • Bước 1: Sửa pg_hba.conf
  • Bước 2: Mở firewall UFW
  • Bước 3: Restart PostgreSQL
  • Bước 4: Cập nhật connection string trong code
  • Tóm tắt nhanh
  • Lưu ý