PostgreSQL database server

ติดตั้งและตั้งค่า PostgreSQL บน VPS สำหรับ Production

มิถุนายน 2026 · อ่าน 8 นาที

PostgreSQL หรือที่เรียกกันว่า "Postgres" คือระบบจัดการฐานข้อมูลเชิงสัมพันธ์แบบ Open Source ที่ได้รับความนิยมสูงสุดตัวหนึ่งของโลก ด้วยความสามารถรองรับข้อมูลขนาดใหญ่ รองรับ ACID transactions และมีฟีเจอร์ขั้นสูงอย่าง JSON, Full-Text Search และ Window Functions ทำให้ PostgreSQL เหมาะสมกับการใช้งานระดับ Production บทความนี้จะพาคุณติดตั้งและตั้งค่า PostgreSQL บน VPS Ubuntu อย่างครบถ้วน ตั้งแต่การติดตั้ง ตั้งค่าความปลอดภัย ปรับแต่ง Performance จนถึงระบบ Backup อัตโนมัติ

สิ่งที่ต้องเตรียม

ขั้นตอนที่ 1 — ติดตั้ง PostgreSQL

Ubuntu มี PostgreSQL ใน official repository แต่เพื่อให้ได้เวอร์ชันล่าสุด แนะนำให้เพิ่ม repository ของ PostgreSQL โดยตรง

# อัพเดท package list
sudo apt update && sudo apt upgrade -y

# ติดตั้ง dependencies
sudo apt install -y curl ca-certificates

# เพิ่ม PostgreSQL APT repository
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc \
  --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc

sudo sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \
  https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
  > /etc/apt/sources.list.d/pgdg.list'

# ติดตั้ง PostgreSQL 16
sudo apt update
sudo apt install -y postgresql-16 postgresql-contrib-16

หลังติดตั้งเสร็จ PostgreSQL จะเริ่มทำงานอัตโนมัติ ตรวจสอบสถานะได้ด้วยคำสั่ง:

sudo systemctl status postgresql
sudo systemctl enable postgresql   # ให้เริ่มอัตโนมัติเมื่อรีบูต

ขั้นตอนที่ 2 — ตั้งค่า Authentication และความปลอดภัย

PostgreSQL ใช้ไฟล์ pg_hba.conf ควบคุมการ authenticate ผู้ใช้ ค่า default ของ Ubuntu ใช้ peer authentication สำหรับ Unix socket ซึ่งปลอดภัยดี แต่ต้องปรับสำหรับการเชื่อมต่อจาก application

# เข้าสู่ psql ด้วย postgres user
sudo -u postgres psql

# ตั้ง password สำหรับ postgres superuser
ALTER USER postgres WITH PASSWORD 'YourStrongPassword123!';
\q

แก้ไขไฟล์ pg_hba.conf:

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

ตัวอย่างการตั้งค่า authentication ที่แนะนำสำหรับ Production:

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# Unix socket connections (สำหรับ local admin)
local   all             postgres                                peer
local   all             all                                     md5

# IPv4 local connections
host    all             all             127.0.0.1/32            scram-sha-256

# อนุญาต application server เฉพาะ IP ที่ระบุ
host    myapp_db        myapp_user      10.0.0.5/32             scram-sha-256
ห้ามตั้งค่า host all all 0.0.0.0/0 md5 เพราะเป็นการเปิดให้ทุก IP เข้าถึงได้ ควรระบุ IP จาก application server เท่านั้น

Restart PostgreSQL เพื่อให้การตั้งค่ามีผล:

sudo systemctl restart postgresql

ขั้นตอนที่ 3 — สร้าง Database และ User

สร้าง database user และ database แยกต่างหากสำหรับแต่ละ application แทนที่จะใช้ postgres superuser ตรงๆ

sudo -u postgres psql

-- สร้าง user พร้อม password
CREATE USER myapp_user WITH PASSWORD 'SecureAppPassword456!';

-- สร้าง database
CREATE DATABASE myapp_db
  WITH OWNER = myapp_user
  ENCODING = 'UTF8'
  LC_COLLATE = 'en_US.UTF-8'
  LC_CTYPE = 'en_US.UTF-8'
  TEMPLATE = template0;

-- ให้สิทธิ์ที่จำเป็น
GRANT CONNECT ON DATABASE myapp_db TO myapp_user;
GRANT USAGE ON SCHEMA public TO myapp_user;
GRANT CREATE ON SCHEMA public TO myapp_user;

\q
หลักการ Least Privilege: ให้สิทธิ์เฉพาะที่จำเป็นแก่ application user เท่านั้น อย่า grant SUPERUSER หรือ CREATEDB ให้ application user ทั่วไป

ขั้นตอนที่ 4 — ปรับแต่ง Performance (postgresql.conf)

ค่า default ของ PostgreSQL ตั้งมาให้ใช้ได้กับ hardware ทั่วไป ต้องปรับให้เหมาะกับขนาด RAM และ workload ของเซิร์ฟเวอร์

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

ตัวอย่างค่าที่แนะนำสำหรับ VPS RAM 4 GB (ปรับตามสัดส่วน):

# Memory Settings
shared_buffers = 1GB               # ประมาณ 25% ของ RAM
effective_cache_size = 3GB         # ประมาณ 75% ของ RAM
work_mem = 16MB                    # ต่อ query operation
maintenance_work_mem = 256MB       # สำหรับ VACUUM, CREATE INDEX

# Checkpoint Settings
checkpoint_completion_target = 0.9
wal_buffers = 16MB
min_wal_size = 1GB
max_wal_size = 4GB

# Connection Settings
max_connections = 100              # ปรับตามจำนวน connection จริง

# Query Planner
random_page_cost = 1.1             # ถ้าใช้ SSD ให้ตั้งเป็น 1.1
effective_io_concurrency = 200     # สำหรับ SSD

# Logging (Production)
log_min_duration_statement = 1000  # log query ที่ช้ากว่า 1 วินาที
log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d,app=%a,client=%h '
ใช้เครื่องมือ PGTune เพื่อคำนวณค่าที่เหมาะสมกับ hardware ของคุณโดยอัตโนมัติ

Reload การตั้งค่าโดยไม่ต้อง restart (สำหรับค่าส่วนใหญ่):

sudo systemctl reload postgresql
# หรือ
sudo -u postgres psql -c "SELECT pg_reload_conf();"

ขั้นตอนที่ 5 — ตั้งค่า Firewall

PostgreSQL ฟังที่ port 5432 โดย default ถ้าไม่ต้องการให้ external IP เข้าถึง ควร block ด้วย UFW:

# ไม่อนุญาต external access (ถ้า app อยู่บน server เดียวกัน)
sudo ufw deny 5432

# หรือถ้า app server อยู่คนละ VPS ให้ระบุ IP
sudo ufw allow from 10.0.0.5 to any port 5432 proto tcp

sudo ufw reload
sudo ufw status

ถ้าต้องการให้ PostgreSQL bind เฉพาะ localhost ให้แก้ไขใน postgresql.conf:

# ใน postgresql.conf
listen_addresses = 'localhost'     # bind เฉพาะ 127.0.0.1

# ถ้าต้องการให้ app server อื่นเชื่อมต่อได้
listen_addresses = 'localhost,10.0.0.1'   # ระบุ IP ที่ต้องการ

ขั้นตอนที่ 6 — ตั้งค่า Backup อัตโนมัติ

การ backup เป็นสิ่งสำคัญที่สุดสำหรับ Production Database สร้าง script backup และตั้ง cron job:

sudo mkdir -p /var/backups/postgresql
sudo chown postgres:postgres /var/backups/postgresql
sudo chmod 750 /var/backups/postgresql

สร้าง backup script:

sudo nano /usr/local/bin/pg_backup.sh
#!/bin/bash
# PostgreSQL Backup Script

BACKUP_DIR="/var/backups/postgresql"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
PGUSER="postgres"

# Backup ทุก database
for DB in $(sudo -u postgres psql -t -c "SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres';"); do
  DB=$(echo $DB | tr -d ' ')
  FILENAME="${BACKUP_DIR}/${DB}_${DATE}.sql.gz"
  sudo -u postgres pg_dump "$DB" | gzip > "$FILENAME"
  echo "[$(date)] Backed up: $DB → $FILENAME"
done

# ลบไฟล์เก่ากว่า RETENTION_DAYS วัน
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
echo "[$(date)] Cleanup: removed backups older than ${RETENTION_DAYS} days"
sudo chmod +x /usr/local/bin/pg_backup.sh

ตั้ง cron job ให้ backup ทุกคืนเวลา 02:00 น.:

sudo crontab -u postgres -e

# เพิ่มบรรทัดนี้
0 2 * * * /usr/local/bin/pg_backup.sh >> /var/log/pg_backup.log 2>&1
สำหรับ Production จริง ควร copy backup ไปยัง remote storage ด้วย เช่น S3, Backblaze B2 หรือ VPS อีกตัว เพื่อป้องกันกรณี disk เสีย

ขั้นตอนที่ 7 — การ Maintenance ที่ควรรู้

VACUUM และ ANALYZE

PostgreSQL มี autovacuum ทำงานอัตโนมัติ แต่สำหรับตารางขนาดใหญ่ที่มีการ update/delete บ่อย อาจต้องรัน manual:

-- ตรวจสอบ dead tuples
SELECT schemaname, relname, n_dead_tup, n_live_tup,
       round(n_dead_tup::numeric/nullif(n_live_tup,0)*100,2) AS dead_ratio
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;

-- VACUUM แบบ verbose
VACUUM VERBOSE ANALYZE mytable;

-- ถ้า bloat สูงมาก ใช้ VACUUM FULL (lock table)
VACUUM FULL ANALYZE mytable;

ตรวจสอบ Slow Queries

-- เปิด pg_stat_statements (ใน postgresql.conf)
-- shared_preload_libraries = 'pg_stat_statements'

-- หลัง restart สร้าง extension
CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

-- ดู Top 10 slow queries
SELECT query,
       calls,
       round(total_exec_time::numeric, 2) AS total_ms,
       round(mean_exec_time::numeric, 2) AS avg_ms,
       rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

ตรวจสอบ Connection และ Lock

-- ดู active connections
SELECT pid, usename, application_name, client_addr,
       state, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE state != 'idle'
ORDER BY query_start;

-- ยกเลิก query ที่ค้างนาน
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE state = 'active'
  AND query_start < NOW() - INTERVAL '5 minutes'
  AND pid != pg_backend_pid();

ขั้นตอนที่ 8 — ทดสอบการเชื่อมต่อ

ทดสอบว่าทุกอย่างทำงานถูกต้อง:

# เชื่อมต่อด้วย app user
psql -U myapp_user -d myapp_db -h 127.0.0.1 -W

# ทดสอบสร้างตารางและ insert ข้อมูล
CREATE TABLE test_table (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

INSERT INTO test_table (name) VALUES ('Hello PostgreSQL');
SELECT * FROM test_table;

DROP TABLE test_table;
ถ้าได้ error FATAL: password authentication failed ให้ตรวจสอบ pg_hba.conf และ restart PostgreSQL อีกครั้ง และตรวจสอบว่า password ที่ตั้งไว้ถูกต้อง

สรุปและ Checklist

คุณได้ติดตั้งและตั้งค่า PostgreSQL สำหรับ Production บน VPS Ubuntu เรียบร้อยแล้ว สิ่งที่ทำไปในบทความนี้ครอบคลุม:

PostgreSQL ที่ตั้งค่าอย่างถูกต้องจะมีความเสถียรและ performance ที่ดีเยี่ยมสำหรับ workload ระดับ Production ขอแนะนำให้ monitor การทำงานอย่างสม่ำเสมอผ่าน log และ pg_stat_activity เพื่อจับปัญหาก่อนที่จะกระทบผู้ใช้งาน

ตรวจสอบว่า PostgreSQL ทำงานถูกต้อง

# ตรวจสอบสถานะ service
sudo systemctl status postgresql

# ตรวจสอบเวอร์ชันและว่า PostgreSQL พร้อมรับ connection
sudo -u postgres psql -c "SELECT version();"

# ทดสอบ connection ด้วย app user
psql -U myapp_user -d myapp_db -h 127.0.0.1 -c "\conninfo"

# ตรวจสอบ autovacuum ทำงานอยู่
sudo -u postgres psql -c "SHOW autovacuum;"

# ดูขนาด database ทั้งหมด
sudo -u postgres psql -c "\l+"
Tip: ใช้คำสั่ง \conninfo ใน psql เพื่อดูว่า connect อยู่กับ database/user/host อะไร มีประโยชน์มากเมื่อ debug connection

แก้ปัญหาที่พบบ่อย

Error: "FATAL: password authentication failed for user"

รหัสผ่านไม่ถูกต้องหรือ authentication method ใน pg_hba.conf ไม่ match

# ตรวจสอบ pg_hba.conf ว่า method ตรงกัน
sudo cat /etc/postgresql/16/main/pg_hba.conf | grep -v "^#" | grep -v "^$"

# Reset password ของ user
sudo -u postgres psql -c "ALTER USER myapp_user WITH PASSWORD 'NewPassword!';"

# ถ้าเปลี่ยน pg_hba.conf ต้อง reload
sudo systemctl reload postgresql

Error: "could not connect to server: Connection refused"

PostgreSQL ไม่ได้ฟังที่ IP/port นั้น หรือ service ไม่ทำงาน

# ตรวจสอบว่า PostgreSQL ทำงานอยู่
sudo systemctl status postgresql

# ดูว่า PostgreSQL ฟัง port ไหน
ss -tlnp | grep 5432

# ตรวจสอบ listen_addresses ใน postgresql.conf
sudo grep listen_addresses /etc/postgresql/16/main/postgresql.conf

Error: "pg_hba.conf: no pg_hba.conf entry for host"

ไม่มีกฎใน pg_hba.conf ที่อนุญาต connection จาก host/user/database นั้น

# เปิดไฟล์แล้วเพิ่มกฎที่ต้องการ
sudo nano /etc/postgresql/16/main/pg_hba.conf

# ตัวอย่าง: อนุญาต myapp_user จาก localhost
# host    myapp_db    myapp_user    127.0.0.1/32    scram-sha-256

# Reload หลังแก้
sudo systemctl reload postgresql

Performance ช้า — Query ใช้เวลานาน

Index หายหรือ statistics ไม่อัพเดท ลองรัน EXPLAIN ANALYZE ก่อน

-- ดูแผนการ query
EXPLAIN ANALYZE SELECT * FROM mytable WHERE user_id = 123;

-- ถ้าไม่มี index บน column ที่ใช้กรอง
CREATE INDEX CONCURRENTLY idx_mytable_user_id ON mytable(user_id);

-- อัพเดท statistics
ANALYZE mytable;

Disk เต็ม — autovacuum ทำงานไม่ได้ / bloat สูง

ถ้า disk เต็ม PostgreSQL จะหยุดรับ write ต้องเพิ่ม space หรือ VACUUM FULL ตาราง bloated

-- ดู disk usage ของแต่ละตาราง
SELECT schemaname, tablename,
       pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 10;

-- VACUUM FULL ตารางที่ใหญ่มาก (lock table ชั่วคราว)
VACUUM FULL ANALYZE bigtable;

คำถามที่พบบ่อย

PostgreSQL ต่างจาก MySQL/MariaDB อย่างไร?

PostgreSQL มีฟีเจอร์ขั้นสูงกว่า เช่น JSONB, Full-Text Search, Window Functions, CTEs, Foreign Data Wrappers และ ACID compliance ที่เข้มงวดกว่า เหมาะกับ workload ที่ต้องการ query ซับซ้อน PostgreSQL นิยมใช้กับ Python (Django/SQLAlchemy) ในขณะที่ MySQL นิยมใช้กับ PHP (WordPress/Laravel) มากกว่า

ควร VACUUM database บ่อยแค่ไหน?

PostgreSQL มี autovacuum ทำงานอัตโนมัติตลอดเวลา ไม่จำเป็นต้อง manual VACUUM บ่อยๆ ยกเว้นกรณีที่ตารางมี update/delete สูงมาก (เช่น queue table หรือ log table) หรือหลังจาก bulk delete ข้อมูลจำนวนมากซึ่ง autovacuum อาจตามไม่ทัน ตรวจสอบ dead tuples จาก pg_stat_user_tables

Schema กับ Database ใน PostgreSQL ต่างกันอย่างไร?

Database คือ container ระดับสูงสุด ซึ่งภายในมีได้หลาย Schema Schema คือ namespace สำหรับจัดกลุ่ม table, function, view ภายใน Database เดียว แนะนำให้แยก Database ต่อ application แต่ใช้ Schema แยกตาม module หรือ microservice ภายใน app เดียวกัน ช่วยควบคุม permission ได้ละเอียด