ติดตั้งและตั้งค่า PostgreSQL บน VPS สำหรับ Production
PostgreSQL หรือที่เรียกกันว่า "Postgres" คือระบบจัดการฐานข้อมูลเชิงสัมพันธ์แบบ Open Source ที่ได้รับความนิยมสูงสุดตัวหนึ่งของโลก ด้วยความสามารถรองรับข้อมูลขนาดใหญ่ รองรับ ACID transactions และมีฟีเจอร์ขั้นสูงอย่าง JSON, Full-Text Search และ Window Functions ทำให้ PostgreSQL เหมาะสมกับการใช้งานระดับ Production บทความนี้จะพาคุณติดตั้งและตั้งค่า PostgreSQL บน VPS Ubuntu อย่างครบถ้วน ตั้งแต่การติดตั้ง ตั้งค่าความปลอดภัย ปรับแต่ง Performance จนถึงระบบ Backup อัตโนมัติ
สิ่งที่ต้องเตรียม
- VPS Ubuntu 22.04 LTS หรือ 24.04 LTS
- สิทธิ์ sudo หรือ root access
- RAM อย่างน้อย 1 GB (แนะนำ 2 GB ขึ้นไปสำหรับ Production)
- Disk Space อย่างน้อย 20 GB
ขั้นตอนที่ 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
ขั้นตอนที่ 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 '
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
ขั้นตอนที่ 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;
FATAL: password authentication failed ให้ตรวจสอบ pg_hba.conf และ restart PostgreSQL อีกครั้ง และตรวจสอบว่า password ที่ตั้งไว้ถูกต้องสรุปและ Checklist
คุณได้ติดตั้งและตั้งค่า PostgreSQL สำหรับ Production บน VPS Ubuntu เรียบร้อยแล้ว สิ่งที่ทำไปในบทความนี้ครอบคลุม:
- ติดตั้ง PostgreSQL 16 จาก official repository
- ตั้งค่า authentication ด้วย scram-sha-256 และ peer
- สร้าง user/database แยกตาม application ด้วยหลัก Least Privilege
- ปรับแต่ง shared_buffers, work_mem และค่า performance อื่นๆ
- ล็อก port 5432 ด้วย UFW ให้เปิดเฉพาะ IP ที่อนุญาต
- ตั้ง cron job backup อัตโนมัติทุกคืนพร้อม retention policy
- ตรวจสอบ slow queries และ connection ด้วย pg_stat_statements
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+"
\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 ได้ละเอียด