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. /Kết nối PostgreSQL trên VPS qua SSH Tunnel từ máy local

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.

Cập nhật: 21 thg 9, 20264 phút đọc
PostgreSQLSSHVPSMạng
Mục lục
  • Tóm tắt nhanh
  • 1. Hiểu mô hình kết nối
  • 2. Tạo SSH Tunnel cho local dev
  • 3. Cấu hình .env.local cho local dev
  • 4. Cấu hình .env.production khi deploy chung VPS
  • 5. Nâng cao: chạy SSH Tunnel ở background
  • 6. Nâng cao: xử lý trùng port
  • Xử lý sự cố

Port 5432 của PostgreSQL trên VPS thường bị tường lửa chặn (và nên như vậy), nên máy dev không kết nối thẳng vào database được. SSH Tunnel giải quyết việc này: nó ánh xạ một port trên máy local sang port 5432 của VPS qua kết nối SSH đã mã hóa. Bài này hướng dẫn mở tunnel và cấu hình .env cho từng môi trường (local / production).

Trong toàn bài, thay <username> bằng user SSH trên VPS, <server-ip> bằng IP VPS, còn <db_user>, <password>, <database_name> là thông tin database của bạn. ~/.ssh/id_ed25519 là đường dẫn private key SSH - đổi lại nếu key của bạn tên khác.

Tóm tắt nhanh

Local - SSH Tunnel:

# Mở terminal riêng, giữ chạy suốt phiên làm việc
ssh -L 5432:localhost:5432 <username>@<server-ip> -i ~/.ssh/id_ed25519 -N
# .env.local
DATABASE_URL=postgresql://<db_user>:<password>@localhost:5432/<database_name>

Production (deploy chung VPS với database):

# .env.production
DATABASE_URL=postgresql://<db_user>:<password>@localhost:5432/<database_name>

Trên production, backend chạy cùng VPS với PostgreSQL nên dùng thẳng localhost - không cần SSH Tunnel.

1. Hiểu mô hình kết nối

+------------------+          SSH Tunnel           +------------------+
|   Máy local      | ---- port 5432 -------------> |   VPS            |
|   (máy dev)      |  ssh -L 5432:localhost:5432   |   PostgreSQL     |
|                  |                               |   :5432          |
+------------------+                               +------------------+
  • Local: máy dev không truy cập thẳng được database trên VPS (port 5432 thường bị firewall chặn) -> dùng SSH Tunnel để ánh xạ port 5432 local sang port 5432 của VPS.
  • Production: backend deploy cùng VPS với database -> dùng localhost trực tiếp.

2. Tạo SSH Tunnel cho local dev

Mở một terminal riêng và chạy:

ssh -L 5432:localhost:5432 <username>@<server-ip> -i ~/.ssh/id_ed25519 -N
FlagÝ nghĩa
-L 5432:localhost:5432Forward port 5432 local sang port 5432 trên VPS
<username>@<server-ip>User SSH và IP của VPS
-i ~/.ssh/id_ed25519Đường dẫn tới private key SSH
-NKhông mở shell, chỉ giữ tunnel

Terminal này phải giữ mở trong suốt phiên làm việc. Đóng terminal là mất kết nối database.

3. Cấu hình .env.local cho local dev

DATABASE_URL=postgresql://<db_user>:<password>@localhost:5432/<database_name>

Vì SSH tunnel đã ánh xạ localhost:5432 sang VPS, backend sẽ kết nối database bình thường như thể database nằm ngay trên máy local.

4. Cấu hình .env.production khi deploy chung VPS

DATABASE_URL=postgresql://<db_user>:<password>@localhost:5432/<database_name>

Khi backend deploy cùng VPS với PostgreSQL, localhost trỏ thẳng tới database trên cùng máy - không cần tunnel hay IP công khai.

5. Nâng cao: chạy SSH Tunnel ở background

Nếu không muốn giữ terminal mở, có thể chạy tunnel ở background:

ssh -f -N -L 5432:localhost:5432 <username>@<server-ip> -i ~/.ssh/id_ed25519
FlagÝ nghĩa
-fChạy ở background sau khi xác thực
-NKhông mở shell

Để tắt tunnel đang chạy background (macOS / Linux):

# Tìm PID
ps aux | grep "ssh -f -N -L"

# Kill process
kill <PID>

Trên Windows (PowerShell):

# Tìm PID
Get-Process ssh | Where-Object { $_.CommandLine -like "*5432*" }

# Kill process
Stop-Process -Id <PID>

6. Nâng cao: xử lý trùng port

Nếu máy local đã có PostgreSQL chạy trên port 5432, cho tunnel dùng port local khác:

ssh -L 5433:localhost:5432 <username>@<server-ip> -i ~/.ssh/id_ed25519 -N

Và cập nhật .env.local:

DATABASE_URL=postgresql://<db_user>:<password>@localhost:5433/<database_name>

Cách ngược lại - giữ tunnel ở 5432 và đổi port của PostgreSQL local - được mô tả trong bài Clone database PostgreSQL từ VPS về máy local Windows.

Xử lý sự cố

Triệu chứngNguyên nhânCách xử lý
Connection refused trên localSSH tunnel chưa chạy hoặc đã bị đóngKiểm tra terminal đang giữ tunnel, chạy lại lệnh SSH
Address already in usePort 5432 local đã bị chiếm (PostgreSQL local đang chạy)Dùng port khác: -L 5433:localhost:5432
Permission denied (publickey)Sai đường dẫn key hoặc key chưa được thêm vào VPSKiểm tra đường dẫn sau -i, đảm bảo public key có trong ~/.ssh/authorized_keys trên VPS
Network is unreachableVPS không online hoặc IP đã thay đổiKiểm tra ping tới VPS, xác nhận lại IP
Tunnel tự ngắt sau một thời gianServer timeout do idleThêm -o ServerAliveInterval=60 vào lệnh SSH

Lệnh đầy đủ để giữ tunnel ổn định:

ssh -L 5432:localhost:5432 <username>@<server-ip> \
  -i ~/.ssh/id_ed25519 \
  -N \
  -o ServerAliveInterval=60 \
  -o ServerAliveCountMax=3
Bài trướcTối ưu hiệu suất VPS: swap, PostgreSQL và DB poolBài tiếp theoClone database PostgreSQL từ VPS về máy local Windows

Bài liên quan

  • 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
  • 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.

    Database

    Database
  • 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.

    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

  • Tóm tắt nhanh
  • 1. Hiểu mô hình kết nối
  • 2. Tạo SSH Tunnel cho local dev
  • 3. Cấu hình .env.local cho local dev
  • 4. Cấu hình .env.production khi deploy chung VPS
  • 5. Nâng cao: chạy SSH Tunnel ở background
  • 6. Nâng cao: xử lý trùng port
  • Xử lý sự cố