Cài đặt PostgreSQL trên VPS an toàn
Cài và cấu hình PostgreSQL trên VPS Ubuntu/Debian cho production: tạo database và user, mở kết nối từ xa an toàn (SSL + pg_hba + UFW), và hiểu transaction isolation.
Mục lục
Bài này cài PostgreSQL trên VPS Ubuntu/Debian theo hướng an toàn cho production: cài đặt, tạo database và user, mở kết nối từ xa có kiểm soát (chỉ đúng IP + bắt buộc SSL), và cuối bài giải thích transaction isolation để bạn chọn đúng mức.
Thay <username>, <password>, <database_name>, <server-ip> bằng giá trị thật
của bạn. IP ví dụ 203.0.113.42 chỉ là placeholder.
1. Cài đặt PostgreSQL
sudo apt update
sudo apt install postgresql postgresql-contrib
sudo systemctl start postgresql.serviceMặc định PostgreSQL KHÔNG tự khởi động lại sau khi VPS reboot. Bật auto-start:
sudo systemctl enable postgresql
sudo service postgresql status2. Tạo database và user
Vào prompt của PostgreSQL:
sudo -u postgres psqlTạo user, đặt vài mặc định (encoding, isolation, timezone) rồi tạo database:
CREATE USER <username> WITH ENCRYPTED PASSWORD '<password>';
ALTER ROLE <username> SET client_encoding TO 'utf8';
ALTER ROLE <username> SET default_transaction_isolation TO 'read committed';
ALTER ROLE <username> SET timezone TO 'UTC';
CREATE DATABASE <database_name>
WITH OWNER = <username>
ENCODING = 'UTF8'
TEMPLATE = template0;
\q3. Mở kết nối từ xa an toàn
Nếu cần kết nối từ máy local hay server khác, làm theo các bước dưới. Nguyên tắc:
chỉ cho đúng IP, bắt buộc SSL, và chặn ở cả tường lửa lẫn pg_hba.conf.
Trên máy cần kết nối, lấy IP public của nó:
curl ifconfig.meGhi lại IP đó (ví dụ 203.0.113.42).
Cấu hình listen address
sudo nano /etc/postgresql/16/main/postgresql.confThay 16 bằng version PostgreSQL của bạn (ls /etc/postgresql/ để xem). Sửa:
# Chỉ nghe trên localhost và IP public của server
listen_addresses = 'localhost,<server-ip>'
port = 5432
# BẮT BUỘC: bật SSL để mã hóa kết nối
ssl = onTUYỆT ĐỐI không dùng listen_addresses = '*' trên production.
Cấu hình pg_hba.conf (quan trọng nhất)
File này quyết định AI được phép kết nối.
sudo nano /etc/postgresql/16/main/pg_hba.confXóa/comment dòng nguy hiểm (nếu có), rồi thêm rule chỉ cho đúng IP + bắt buộc SSL vào cuối file:
# NGUY HIỂM - cho phép mọi IP: host all all 0.0.0.0/0 md5
# Rule an toàn: chỉ đúng IP + bắt buộc SSL
hostssl all all 203.0.113.42/32 scram-sha-256Ý nghĩa từng cột:
| Thành phần | Nghĩa |
|---|---|
hostssl | Bắt buộc kết nối SSL |
all (thứ nhất) | Áp cho mọi database |
all (thứ hai) | Áp cho mọi user |
203.0.113.42/32 | Chỉ đúng IP này (/32 = một IP) |
scram-sha-256 | Phương thức xác thực mạnh nhất |
Mẹo: thay all bằng tên database và user cụ thể để siết phạm vi hơn nữa.
Tường lửa UFW + restart
# Chỉ cho đúng IP vào port 5432
sudo ufw allow from 203.0.113.42 to any port 5432
# Chặn mọi IP khác
sudo ufw deny 5432
sudo ufw status
sudo systemctl restart postgresqlHai lớp bảo vệ (UFW + pg_hba.conf) bổ trợ nhau.
4. Kiểm tra kết nối
Xác nhận PostgreSQL đang nghe đúng IP:
sudo ss -lntp | grep 5432Thử kết nối từ máy local:
psql -h <server-ip> -U <username> -d <database_name>Phụ lục: Transaction isolation
Khi nhiều request/user cùng đọc-ghi database một lúc, có thể gặp: dirty read (đọc dữ liệu chưa commit), non-repeatable read (đọc cùng bản ghi ra kết quả khác nhau), phantom read (truy vấn sau xuất hiện thêm bản ghi "ma"), lost update (hai request ghi đè nhau). Isolation level quyết định cho phép hay chặn các hiện tượng này.
| Mức | Dirty read | Non-repeatable | Phantom | Hiệu năng |
|---|---|---|---|---|
READ UNCOMMITTED | Không (PostgreSQL map về READ COMMITTED) | Có thể | Có thể | Cao |
READ COMMITTED | Không | Có thể | Có thể | Cao |
REPEATABLE READ | Không | Không | Có thể | Trung bình |
SERIALIZABLE | Không | Không | Không | Chậm |
PostgreSQL mặc định READ COMMITTED - hợp cho khoảng 90% ứng dụng. Với mức này,
mỗi SELECT chỉ thấy dữ liệu đã commit, không thấy dữ liệu transaction khác đang
sửa mà chưa commit:
Bảng accounts: id=1, balance=1000
Transaction A Transaction B
BEGIN;
UPDATE accounts
SET balance = 500
WHERE id = 1;
-- chưa COMMIT
SELECT balance FROM accounts WHERE id = 1;
--> 1000 (không thấy dữ liệu chưa commit)
COMMIT;
SELECT balance FROM accounts WHERE id = 1;
--> 500Chọn mức nào:
| Trường hợp | Khuyến nghị |
|---|---|
| API CRUD thông thường | READ COMMITTED |
| Báo cáo, thống kê | READ COMMITTED |
| Đơn hàng, đặt chỗ | REPEATABLE READ hoặc SELECT ... FOR UPDATE |
| Tài chính, kế toán | SERIALIZABLE |
Vì sao nên SET isolation cho ROLE? PostgreSQL vẫn mặc định READ COMMITTED,
nhưng app/migration/script có thể tự đổi mức khác. Đặt cố định ở ROLE đảm bảo mọi
kết nối của user đó hành xử nhất quán, tránh việc vô tình đổi mức - dễ debug và dự
đoán hơn.
ALTER ROLE app_user SET default_transaction_isolation TO 'read committed';