
ทุกครั้งที่แก้โค้ดแล้วต้องเปิด FTP อัพโหลดทีละไฟล์ หรือ SSH เข้าไป Pull เอง — มันน่าเบื่อและเสียเวลา แถมยังเสี่ยง Human Error อีก ถ้าคุณมี VPS อยู่แล้ว คุณสามารถตั้งค่า Git Auto Deploy ให้ทุก git push อัพโหลดโค้ดขึ้นเซิร์ฟเวอร์โดยอัตโนมัติได้เลย
บทความนี้สอนวิธีสร้าง Git Bare Repository + post-receive Hook บน VPS Ubuntu ตั้งแต่ต้นจนจบ ใช้งานได้กับโปรเจกต์ HTML, PHP, Node.js หรือ Python และเป็นพื้นฐานของ CI/CD สมัยใหม่
สิ่งที่ต้องมีก่อน: VPS Ubuntu 20.04/22.04 ที่ติดตั้ง Git แล้ว, SSH Access, Nginx หรือ Apache พร้อมใช้งาน, Git ติดตั้งบนเครื่องส่วนตัวแล้ว
Git Deploy ทำงานอย่างไร?
หลักการคือเราสร้าง Bare Repository บน VPS เพื่อรับ Push จากเครื่องส่วนตัว แล้วใช้ Git Hook ชื่อ post-receive เพื่อสั่งให้ Git Checkout โค้ดล่าสุดไปยัง Web Root โดยอัตโนมัติ ทุกครั้งที่มี Push ใหม่เข้ามา
| ขั้นตอน | คำอธิบาย |
|---|---|
| 1. Bare Repo | สร้าง Git Repository บน VPS (ไม่มี Working Tree) สำหรับรับ Push |
| 2. post-receive | Shell Script ที่รันอัตโนมัติหลัง Push — สั่ง Checkout ไฟล์ไปยัง Web Root |
| 3. Remote | เพิ่ม VPS เป็น Git Remote บนเครื่องส่วนตัว |
| 4. git push | Push โค้ดขึ้น VPS — Hook ทำงาน — เว็บอัพเดทอัตโนมัติ |
ขั้นที่ 1 — เตรียม VPS และโครงสร้างโฟลเดอร์
SSH เข้า VPS แล้วสร้างโฟลเดอร์ 2 อัน ได้แก่ Bare Repository และ Web Root:
# โฟลเดอร์รับ Push (Bare Repository) mkdir -p /home/deploy/repos/mysite.git # โฟลเดอร์ที่ Nginx/Apache เสิร์ฟไฟล์จริง mkdir -p /var/www/mysite
ทำไมต้อง 2 โฟลเดอร์? Bare Repository เก็บ Git Object เท่านั้น (ไม่มีไฟล์โปรเจกต์) Web Root คือที่เก็บไฟล์จริงที่ Web Server อ่าน — เป็น Best Practice ที่แยก Git Storage ออกจาก Public Files
ขั้นที่ 2 — สร้าง Bare Repository
cd /home/deploy/repos/mysite.git git init --bare
จะได้ผลลัพธ์ว่า Initialized empty Git repository in /home/deploy/repos/mysite.git/ โครงสร้างภายในจะมีแค่ HEAD, config, objects/, refs/ ไม่มีไฟล์โปรเจกต์
ขั้นที่ 3 — สร้าง post-receive Hook
Hook คือ Script ที่ Git รันอัตโนมัติเมื่อเกิด Event ต่างๆ เราจะใช้ post-receive ซึ่งทำงาน หลังรับ Push สำเร็จ:
nano /home/deploy/repos/mysite.git/hooks/post-receive
วางเนื้อหานี้ลงในไฟล์:
#!/bin/bash GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main echo "✅ Deploy สำเร็จ → /var/www/mysite"
บันทึกไฟล์ แล้วให้สิทธิ์รัน:
chmod +x /home/deploy/repos/mysite.git/hooks/post-receive
สำคัญ: ถ้า Branch หลักของคุณเป็น master ให้เปลี่ยน main เป็น master ในบรรทัด git checkout ด้วย
ขั้นที่ 4 — ตั้งค่าสิทธิ์ Web Root
ให้แน่ใจว่า User ที่รัน Web Server มีสิทธิ์อ่านไฟล์ใน Web Root (ปกติคือ www-data สำหรับ Nginx/Apache บน Ubuntu):
chown -R www-data:www-data /var/www/mysite chmod -R 755 /var/www/mysite
ถ้าคุณ Deploy ด้วย User deploy แทน root ให้เพิ่ม User นั้นเข้า Group www-data:
usermod -aG www-data deploy
ขั้นที่ 5 — เพิ่ม VPS เป็น Remote บนเครื่องส่วนตัว
บนเครื่องของคุณ (ไม่ใช่ VPS) ให้ไปที่โฟลเดอร์โปรเจกต์และเพิ่ม Remote ชื่อ production:
# ถ้ายังไม่มี Git Repo บนเครื่องตัวเอง git init git add . git commit -m "Initial commit" # เพิ่ม VPS เป็น Remote (แทนที่ YOUR_VPS_IP) git remote add production ssh://root@YOUR_VPS_IP/home/deploy/repos/mysite.git
ใช้ SSH Key แทน Password: ถ้าตั้งค่า SSH Key Authentication ไว้แล้ว การ Push จะไม่ถามรหัสผ่านทุกครั้ง ดูบทความ วิธีเชื่อมต่อ VPS ด้วย SSH สำหรับการตั้งค่า SSH Key
ขั้นที่ 6 — Deploy ครั้งแรก
Push โค้ดขึ้น VPS ครั้งแรก:
git push production main
ถ้าทุกอย่างถูกต้องจะเห็นผลลัพธ์แบบนี้:
Counting objects: 5, done. Writing objects: 100% (5/5), 512 bytes | 512.00 KiB/s, done. remote: ✅ Deploy สำเร็จ → /var/www/mysite To ssh://root@YOUR_VPS_IP/home/deploy/repos/mysite.git * [new branch] main -> main
ลองเช็คว่าไฟล์ถูก Checkout มาแล้วจริง:
ls -la /var/www/mysite
ขั้นที่ 7 — ตั้งค่า Nginx ให้ชี้ไป Web Root
ถ้ายังไม่ได้ตั้ง Virtual Host ให้ Nginx ให้สร้าง Config ใหม่:
nano /etc/nginx/sites-available/mysite
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
root /var/www/mysite;
index index.html index.php;
location / {
try_files $uri $uri/ =404;
}
}# เปิดใช้งาน Config
ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginxขั้นที่ 8 — Workflow ประจำวันหลังจากนี้
เมื่อตั้งค่าครบแล้ว การ Deploy ทุกครั้งต่อจากนี้ทำแค่นี้:
# แก้โค้ด commit แล้ว push ขึ้น
git add .
git commit -m "แก้ไข header nav"
git push production mainระบบจะ Checkout ไฟล์ใหม่ไปยัง Web Root ทันที เว็บอัพเดทโดยที่คุณไม่ต้องทำอะไรเพิ่มเติม
ความปลอดภัยของ Git Deploy — สิ่งที่ต้องตั้งค่าให้ครบ
Git Deploy ที่ตั้งค่าผิดอาจเปิดช่องโหว่ให้ผู้ไม่หวังดีเข้าถึงเซิร์ฟเวอร์ได้ ควรปฏิบัติตามแนวทางเหล่านี้ทุกครั้งที่ Deploy บน VPS Production:
1. ใช้ SSH Key แทน Password เสมอ
รหัสผ่านถูกเดาหรือ Brute Force ได้ SSH Key ปลอดภัยกว่ามาก ให้ปิดการ Login ด้วย Password สำหรับ SSH:
# แก้ไฟล์ /etc/ssh/sshd_config PasswordAuthentication no PubkeyAuthentication yes # รีสตาร์ท SSH service systemctl restart sshd
2. สร้าง User แยกสำหรับ Deploy
ไม่ควรใช้ root สำหรับ Git Deploy ให้สร้าง User ชื่อ deploy และให้สิทธิ์เฉพาะที่จำเป็น:
# สร้าง user deploy ไม่มี shell login adduser --disabled-login --gecos "" deploy # ให้สิทธิ์เขียนไปยัง Bare Repository และ Web Root chown -R deploy:deploy /home/deploy/repos/ chown -R deploy:www-data /var/www/mysite chmod -R 775 /var/www/mysite
3. จำกัด SSH Key ให้รันแค่ Git Commands
เพิ่มตัวเลือก command= ใน authorized_keys เพื่อจำกัดว่า Key นี้ทำได้แค่ Git Commands เท่านั้น:
# ใน /home/deploy/.ssh/authorized_keys
command="git-shell -c \"$SSH_ORIGINAL_COMMAND\"",no-port-forwarding,no-X11-forwarding ssh-rsa AAAA... your-deploy-keyChecklist ความปลอดภัยก่อน Deploy Production: ปิด PasswordAuthentication, ใช้ SSH Key เท่านั้น, ตั้ง Firewall (UFW), ใช้ User แยก, ตรวจสอบ Log ด้วย journalctl -u sshd เป็นประจำ
การตั้งค่า Environment Variables และ Secrets ใน Hook
โปรเจกต์สมัยใหม่มักต้องใช้ Environment Variables เช่น Database Password, API Key หรือ Secret ต่างๆ ซึ่งไม่ควรเก็บไว้ใน Git Repository การจัดการ Secrets อย่างถูกวิธีใน Hook มีหลายแนวทาง:
วิธีที่ 1: ใช้ไฟล์ .env บน Server
สร้างไฟล์ .env บน VPS โดยตรงและอ่านค่าใน Hook:
#!/bin/bash GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main # โหลด Environment Variables จากไฟล์ .env ที่อยู่นอก Web Root source /home/deploy/secrets/mysite.env # รัน Migration (ตัวอย่าง Laravel) cd /var/www/mysite php artisan migrate --force echo "✅ Deploy พร้อม .env โหลดแล้ว"
สร้างไฟล์ Secrets ในตำแหน่งที่ Web Server ไม่เข้าถึงได้:
mkdir -p /home/deploy/secrets chmod 700 /home/deploy/secrets # เขียน .env file nano /home/deploy/secrets/mysite.env # ตัวอย่างเนื้อหา .env export DB_HOST=localhost export DB_NAME=mysite_db export DB_USER=mysite_user export DB_PASS=your_secure_password export APP_KEY=base64:xxxxxxxxxxxxx
วิธีที่ 2: Symlink .env หลัง Checkout
ถ้าโปรเจกต์ใช้ไฟล์ .env โดยตรง ให้เพิ่มขั้นตอน Symlink ใน Hook:
#!/bin/bash
GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main
# สร้าง Symlink ไปยัง .env จริงที่อยู่นอก Repo
ln -sf /home/deploy/secrets/mysite.env /var/www/mysite/.env
echo "✅ Deploy + .env linked"ห้ามทำ: ไม่ควรเพิ่ม .env ลงใน Git Repository เด็ดขาด แม้จะเป็น Private Repository ก็ตาม เพราะประวัติ Git เก็บข้อมูลทุก Commit ถ้า Secret หลุดเข้า Repo ต้องเปลี่ยน Secret ทันทีและล้าง Git History
เพิ่ม GitHub เป็น Remote คู่ขนาน
ถ้าต้องการ Push ขึ้น GitHub ด้วยพร้อมกัน ให้เพิ่ม Remote ที่ชื่อ origin แยกออกมา:
# Push ขึ้น GitHub (เก็บ Backup + Code Review) git remote add origin https://github.com/youruser/mysite.git # Push ทั้งสอง Remote พร้อมกัน git push origin main git push production main
หรือตั้งค่า Push URL หลายอันให้ origin จัดการให้อัตโนมัติ:
git remote set-url --add --push origin https://github.com/youruser/mysite.git
git remote set-url --add --push origin ssh://root@YOUR_VPS_IP/home/deploy/repos/mysite.git
# Push ครั้งเดียว ไปทั้ง GitHub และ VPS
git push origin mainเพิ่ม Build Step ใน Hook (สำหรับ Node.js / PHP)
สำหรับโปรเจกต์ที่ต้องมีขั้นตอน Build เพิ่มได้ใน Hook โดยตรง:
ตัวอย่างสำหรับ Node.js
#!/bin/bash GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main cd /var/www/mysite npm install --production pm2 restart mysite || pm2 start app.js --name mysite echo "✅ Deploy + restart PM2 สำเร็จ"
ตัวอย่างสำหรับ PHP Composer
#!/bin/bash GIT_WORK_TREE=/var/www/mysite GIT_DIR=/home/deploy/repos/mysite.git git checkout -f main cd /var/www/mysite composer install --no-dev --optimize-autoloader echo "✅ Deploy + Composer install สำเร็จ"
เปรียบเทียบ Git Deploy กับวิธี Deploy อื่นๆ
ก่อนตัดสินใจใช้ Git Deploy ควรทำความเข้าใจว่ามันเหมาะกับงานประเภทใด และข้อจำกัดคืออะไร เทียบกับทางเลือกอื่นที่นิยมใช้:
| วิธี Deploy | ความยาก | เหมาะกับ | ข้อจำกัด |
|---|---|---|---|
| Git Bare + Hook | ง่าย-ปานกลาง | VPS เดียว, ทีมเล็ก | ไม่มี Rollback อัตโนมัติ |
| GitHub Actions | ปานกลาง | ต้องการ Test ก่อน Deploy | ต้องใช้ GitHub, มี Limit |
| FTP Manual | ง่ายมาก | Shared Hosting, เว็บเล็ก | ช้า, เสี่ยง Human Error |
| rsync + SSH | ง่าย | Static site, ไม่ต้องการ History | ไม่มี Version Control |
| Docker + Registry | ยาก | Microservices, หลาย Server | ต้องการความรู้ Docker |
สำหรับ Developer ที่มี VPS และโปรเจกต์ขนาดกลางถึงใหญ่ Git Bare + Hook เป็นจุดเริ่มต้นที่ดีที่สุดเพราะตั้งค่าได้ใน 30 นาที ไม่ต้องพึ่งบริการภายนอก และยกระดับเป็น CI/CD เต็มรูปแบบได้ในภายหลัง
แก้ปัญหาที่พบบ่อย
Push แล้วไม่มีไฟล์ใน Web Root
ตรวจสอบว่า Branch Name ตรงกันระหว่างเครื่องส่วนตัวและ Hook (main vs master) และตรวจ Log ของ Hook:
cat /home/deploy/repos/mysite.git/hooks/post-receive
Permission Denied เมื่อ Push
ตรวจสอบว่า SSH Key ถูกต้อง หรือ User ที่ใช้ SSH มีสิทธิ์เขียนไปยัง Bare Repository:
ls -la /home/deploy/repos/
เว็บแสดง 403 หลัง Deploy
ปัญหา Permission ของ Web Root — ตรวจสอบและแก้ด้วย:
chown -R www-data:www-data /var/www/mysite
find /var/www/mysite -type d -exec chmod 755 {} \;
find /var/www/mysite -type f -exec chmod 644 {} \;ยกระดับ Git Deploy เป็น CI/CD Pipeline ด้วย GitHub Actions
เมื่อคุ้นกับ Git Bare + Hook แล้ว ขั้นต่อไปคือเพิ่มขั้นตอน Test และ Validation อัตโนมัติก่อน Deploy ทุกครั้ง ด้วย GitHub Actions ซึ่งเป็นบริการ CI/CD ที่ใช้งานง่ายและฟรีสำหรับ Public Repository:
ตัวอย่าง Workflow สำหรับ PHP Project
สร้างไฟล์ .github/workflows/deploy.yml ในโปรเจกต์:
name: Test and Deploy
on:
push:
branches: [ main ]
jobs:
test-and-deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- name: Install dependencies
run: composer install --no-dev --optimize-autoloader
- name: Run tests
run: php vendor/bin/phpunit
- name: Deploy to VPS
if: success()
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.VPS_HOST }}
username: deploy
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /home/deploy/repos/mysite.git
git fetch origin main
GIT_WORK_TREE=/var/www/mysite git checkout -f mainด้วย Workflow นี้ เมื่อ Push ขึ้น GitHub ระบบจะ:
- รัน Unit Test อัตโนมัติ — ถ้า Test Fail โค้ดจะไม่ถูก Deploy
- Deploy ขึ้น VPS ผ่าน SSH เฉพาะเมื่อ Test ผ่านทั้งหมด
- ส่ง Notification ถ้า Deploy สำเร็จหรือ Fail
GitHub Actions Secrets: เก็บ VPS_HOST และ VPS_SSH_KEY ใน GitHub Repository Settings → Secrets — ไม่ต้องใส่ข้อมูล Sensitive ใน Code
ก้าวต่อไป: เมื่อคุ้นกับ Git Deploy แล้ว สามารถยกระดับเป็น CI/CD Pipeline ด้วย GitHub Actions หรือ GitLab CI ที่รัน Test อัตโนมัติก่อน Deploy ทุกครั้ง ทำให้มั่นใจว่าโค้ดที่ขึ้น Production ผ่าน Test แล้วเสมอ
ต้องการ VPS สำหรับ Git Deploy?
AsiaGB มี VPS ไทย และ สิงคโปร์ พร้อม Full Root Access เริ่มต้น 500 บาท/เดือน
ดู VPS ทั้งหมด →