- ติดตั้ง Caddy Server ด้วย Docker Compose พร้อม Reverse Proxy และ HTTPS อัตโนมัติ
- 1. สร้าง Docker Compose
- 2. สร้าง Caddyfile สำหรับ Local Development
- 3. เริ่มต้นระบบ
- 4. ดู Log
- 5. ใช้งาน Caddy กับ Domain
- 6. Automatic HTTPS ทำงานอย่างไร
- 7. ตั้งค่า DNS
- 8. เปิด Firewall
- 9. ตรวจสอบ Caddyfile
- 10. Reload Caddy
- 11. Reverse Proxy หลาย Application
- 12. Reverse Proxy ตาม URL Path
- 13. ใช้ Caddy กับ Laravel
- 14. ใช้ Caddy กับ Laravel Octane
- 15. Reverse Proxy ไปยัง Application บน Host
- 16. เพิ่ม Security Headers
- 17. เปิด Access Log
- 18. Health Check
- 19. อัปเดต Caddy
- 20. Backup Caddy Volume
- คำสั่ง Docker Compose ที่ใช้บ่อย
- ข้อควรระวัง
- Production Architecture ที่แนะนำ
- ตัวอย่าง Configuration สำหรับ Production
- สรุป
#ติดตั้ง Caddy Server ด้วย Docker Compose พร้อม Reverse Proxy และ HTTPS อัตโนมัติ
Caddy เป็น Web Server และ Reverse Proxy ที่ได้รับความนิยมในงาน DevOps เพราะตั้งค่าได้ง่าย รองรับ Automatic HTTPS และสามารถจัดการ TLS Certificate ให้อัตโนมัติ ทำให้เหมาะสำหรับการ Deploy Web Application, REST API และบริการที่ทำงานอยู่ภายใน Docker
บทความนี้จะอธิบายตั้งแต่การสร้าง Docker Compose, การเขียน Caddyfile, การทำ Reverse Proxy, การเปิดใช้งาน HTTPS ไปจนถึงแนวทางสำหรับนำไปใช้ใน Production
#Caddy Server คืออะไร
Caddy คือ Open Source Web Server ที่สามารถใช้เป็นทางเลือกแทน Nginx หรือ Apache ได้ โดยมีความสามารถสำคัญ เช่น
- Web Server
- Reverse Proxy
- Automatic HTTPS
- TLS Certificate Management
- HTTP/2 และ HTTP/3
- Load Balancing
- Gzip และ Zstandard Compression
- Access Logging
- Security Headers
- Docker และ Docker Compose Integration
จุดเด่นสำคัญคือ เมื่อกำหนดชื่อโดเมนใน Caddyfile และตั้งค่า DNS ให้ชี้มายัง Server แล้ว Caddy สามารถขอ ติดตั้ง และต่ออายุ TLS Certificate ให้โดยอัตโนมัติ
#ภาพรวมสถาปัตยกรรม
ตัวอย่างในบทความนี้ใช้ Caddy เป็น Reverse Proxy อยู่ด้านหน้า Web Application
Internet
|
v
+-------------------+
| Caddy Server |
| :80 / :443 |
+-------------------+
|
v
+-------------------+
| Web Application |
| web:80 |
+-------------------+
เมื่อผู้ใช้เข้าเว็บไซต์
https://example.com
Request จะเข้ามายัง Caddy ก่อน จากนั้น Caddy จะส่งต่อ Request ไปยัง Container ของ Application ภายใน Docker Network
#สิ่งที่ต้องเตรียม
ก่อนเริ่มต้นควรมี
- Docker
- Docker Compose Plugin
- Linux Server หรือ Cloud VM
- Public IP สำหรับใช้งาน Production
- Domain Name หากต้องการ Automatic HTTPS
- Firewall ที่เปิดพอร์ต
80และ443
ตรวจสอบ Docker:
docker --version
ตรวจสอบ Docker Compose:
docker compose version
#โครงสร้างโปรเจกต์
สร้างโครงสร้างไฟล์ดังนี้
caddy-server/
├── compose.yaml
└── caddy/
└── Caddyfile
สร้างโฟลเดอร์:
mkdir -p caddy-server/caddy
cd caddy-server
#1. สร้าง Docker Compose
สร้างไฟล์ชื่อ
compose.yaml
แล้วเพิ่ม Configuration:
services:
web:
image: nginx:alpine
container_name: demo-web
restart: unless-stopped
networks:
- web-network
caddy:
image: caddy:2-alpine
container_name: caddy-server
restart: unless-stopped
cap_add:
- NET_ADMIN
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
depends_on:
- web
networks:
- web-network
volumes:
caddy_data:
caddy_config:
networks:
web-network:
driver: bridge
#อธิบาย Service web
web:
image: nginx:alpine
ตัวอย่างนี้ใช้ Nginx Container เป็น Web Application สำหรับทดสอบ
restart: unless-stopped
หมายถึง Container จะเริ่มใหม่โดยอัตโนมัติ หากระบบ Restart หรือ Container หยุดทำงานโดยไม่ตั้งใจ
#อธิบาย Service caddy
caddy:
image: caddy:2-alpine
ใช้ Official Caddy Docker Image
พอร์ตที่เปิดใช้งาน:
ports:
- "80:80"
- "443:443"
- "443:443/udp"
| Port | Protocol | หน้าที่ |
|---|---|---|
| 80 | TCP | HTTP |
| 443 | TCP | HTTPS |
| 443 | UDP | HTTP/3 |
#Caddy Volume
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
Volume สำคัญประกอบด้วย
#/etc/caddy/Caddyfile
ใช้เก็บ Configuration หลักของ Caddy
#/data
ใช้เก็บข้อมูล เช่น
- TLS Certificate
- Private Key
- ACME Account
- Certificate Metadata
ดังนั้น caddy_data ไม่ควรถูกลบโดยไม่จำเป็นใน Production
#/config
ใช้เก็บ Configuration ภายในของ Caddy
#2. สร้าง Caddyfile สำหรับ Local Development
สร้างไฟล์
caddy/Caddyfile
และเพิ่ม
:80 {
reverse_proxy web:80
}
ความหมายคือ
Client
|
v
Caddy :80
|
v
web:80
ภายใน Docker Network สามารถเรียก Container อื่นผ่านชื่อ Service ได้
ดังนั้นให้ใช้
web:80
ไม่ควรใช้
localhost:80
เพราะ localhost ภายใน Caddy Container หมายถึงตัว Caddy Container เอง
#3. เริ่มต้นระบบ
ใช้คำสั่ง
docker compose up -d
ตรวจสอบ Container:
docker compose ps
ตัวอย่างผลลัพธ์:
NAME IMAGE STATUS
caddy-server caddy:2-alpine Up
demo-web nginx:alpine Up
เปิด Browser:
http://localhost
หาก Configuration ถูกต้อง จะเห็นหน้า Welcome ของ Nginx
#4. ดู Log
ดู Log ของ Caddy:
docker compose logs -f caddy
ดู Log ทุก Container:
docker compose logs -f
ดูเฉพาะ 100 บรรทัดล่าสุด:
docker compose logs --tail=100 caddy
#5. ใช้งาน Caddy กับ Domain
สำหรับ Production สามารถกำหนด Domain โดยตรงใน Caddyfile
ตัวอย่าง:
example.com {
encode zstd gzip
reverse_proxy web:80
log
}
หรือใช้ Subdomain:
app.example.com {
encode zstd gzip
reverse_proxy web:80
log
}
เมื่อ Caddy ตรวจพบชื่อ Domain ระบบจะพยายามเปิดใช้งาน HTTPS โดยอัตโนมัติ
#6. Automatic HTTPS ทำงานอย่างไร
เมื่อกำหนด
example.com {
reverse_proxy web:80
}
Caddy จะจัดการงานสำคัญ เช่น
- ตรวจสอบ Domain
- ขอ TLS Certificate
- ติดตั้ง Certificate
- เปิด HTTPS
- Redirect HTTP ไป HTTPS
- ต่ออายุ Certificate อัตโนมัติ
ดังนั้นไม่จำเป็นต้องติดตั้ง Certbot สำหรับกรณีทั่วไป
#7. ตั้งค่า DNS
สมมติ Server มี IP
203.0.113.10
กำหนด A Record:
Type: A
Name: @
Value: 203.0.113.10
สำหรับ Subdomain:
Type: A
Name: app
Value: 203.0.113.10
หลังจากนั้นสามารถเข้า
https://app.example.com
ตรวจสอบ DNS:
nslookup app.example.com
หรือ
dig app.example.com
#8. เปิด Firewall
สำหรับ Ubuntu ที่ใช้ UFW:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 443/udp
sudo ufw reload
ตรวจสอบ:
sudo ufw status
หากใช้ Cloud Provider เช่น
- AWS
- DigitalOcean
- Azure
- Google Cloud
ต้องเปิด Firewall หรือ Security Group ของ Cloud Platform ด้วย
#9. ตรวจสอบ Caddyfile
หลังแก้ไข Configuration ควรตรวจสอบ Syntax ก่อนใช้งาน
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
หาก Configuration ถูกต้อง จะไม่มี Error ที่ทำให้ Caddy ไม่สามารถโหลด Configuration ได้
#10. Reload Caddy
หลังแก้ไข Caddyfile สามารถ Reload ได้โดยไม่ต้อง Restart Container
docker compose exec -w /etc/caddy caddy caddy reload
หรือ
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile --adapter caddyfile
#11. Reverse Proxy หลาย Application
Caddy หนึ่งตัวสามารถให้บริการหลายเว็บไซต์หรือหลาย API ได้
ตัวอย่าง Architecture:
+--> frontend:80
|
Internet --> Caddy -+
|
+--> backend:8000
ตัวอย่าง compose.yaml:
services:
frontend:
image: nginx:alpine
restart: unless-stopped
networks:
- web-network
backend:
image: your-backend-api:latest
restart: unless-stopped
expose:
- "8000"
networks:
- web-network
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- web-network
volumes:
caddy_data:
caddy_config:
networks:
web-network:
#Caddyfile
example.com {
encode zstd gzip
reverse_proxy frontend:80
}
api.example.com {
encode zstd gzip
reverse_proxy backend:8000
}
ผลลัพธ์คือ
https://example.com
|
v
frontend:80
และ
https://api.example.com
|
v
backend:8000
#12. Reverse Proxy ตาม URL Path
Caddy สามารถ Route Request ตาม Path ได้
example.com {
encode zstd gzip
handle /api/* {
reverse_proxy backend:8000
}
handle {
reverse_proxy frontend:80
}
}
เช่น
https://example.com
จะส่งไป
frontend:80
ส่วน
https://example.com/api/users
จะส่งไป
backend:8000
#13. ใช้ Caddy กับ Laravel
หาก Laravel ทำงานผ่าน Nginx Container:
example.com {
reverse_proxy nginx:80
}
Architecture:
Internet
|
Caddy
|
Nginx
|
PHP-FPM
|
Laravel
กรณี Caddy ทำหน้าที่แทน Nginx และเชื่อมต่อ PHP-FPM โดยตรง สามารถใช้
example.com {
root * /var/www/html/public
encode zstd gzip
php_fastcgi php:9000
file_server
}
#14. ใช้ Caddy กับ Laravel Octane
หาก Laravel Octane ทำงานที่พอร์ต 8000
example.com {
encode zstd gzip
reverse_proxy laravel:8000
}
Docker Compose:
services:
laravel:
build: .
expose:
- "8000"
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
#15. Reverse Proxy ไปยัง Application บน Host
ในบางกรณี Application ไม่ได้อยู่ใน Docker แต่ทำงานบน Host เช่น
localhost:8080
บน Linux สามารถเพิ่ม
extra_hosts:
- "host.docker.internal:host-gateway"
ตัวอย่าง:
caddy:
image: caddy:2-alpine
extra_hosts:
- "host.docker.internal:host-gateway"
จากนั้นกำหนด
example.com {
reverse_proxy host.docker.internal:8080
}
#16. เพิ่ม Security Headers
สามารถเพิ่ม HTTP Security Headers ได้
example.com {
encode zstd gzip
header {
X-Content-Type-Options "nosniff"
X-Frame-Options "SAMEORIGIN"
Referrer-Policy "strict-origin-when-cross-origin"
Strict-Transport-Security "max-age=31536000; includeSubDomains"
-Server
}
reverse_proxy web:80
}
คำสั่ง
-Server
ใช้ลบ Server Header
#17. เปิด Access Log
ตัวอย่าง Console Log:
example.com {
reverse_proxy web:80
log {
output stdout
format console
}
}
ดู Log:
docker compose logs -f caddy
สำหรับระบบ Logging เช่น Loki หรือ Elasticsearch สามารถใช้ JSON
example.com {
reverse_proxy web:80
log {
output stdout
format json
}
}
#18. Health Check
สามารถเพิ่ม Health Check ให้ Caddy
caddy:
image: caddy:2-alpine
healthcheck:
test:
- CMD
- caddy
- version
interval: 30s
timeout: 10s
retries: 3
start_period: 10s
ตรวจสอบ:
docker compose ps
#19. อัปเดต Caddy
ดึง Image ใหม่:
docker compose pull caddy
Deploy:
docker compose up -d
ตรวจสอบ Version:
docker compose exec caddy caddy version
ใน Production แนะนำให้ Pin Version ของ Image ให้ชัดเจน เช่น
image: caddy:2.11-alpine
แทนการพึ่ง Tag ที่อาจเปลี่ยนแปลงตามเวลา
#20. Backup Caddy Volume
ตรวจสอบ Volume:
docker volume ls
Backup:
docker run --rm -v caddy-server_caddy_data:/data -v "$(pwd)":/backup alpine tar czf /backup/caddy-data-backup.tar.gz -C /data .
จะได้ไฟล์:
caddy-data-backup.tar.gz
#คำสั่ง Docker Compose ที่ใช้บ่อย
#Start
docker compose up -d
#Status
docker compose ps
#Log
docker compose logs -f caddy
#Validate
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
#Reload
docker compose exec -w /etc/caddy caddy caddy reload
#Restart
docker compose restart caddy
#Stop
docker compose down
#Update
docker compose pull
docker compose up -d
#Resource Usage
docker stats
#ข้อควรระวัง
#1. อย่าใช้ localhost ระหว่าง Container
ควรใช้
reverse_proxy web:80
แทน
reverse_proxy localhost:80
#2. อย่าลบ Caddy Volume โดยไม่จำเป็น
คำสั่ง
docker compose down -v
จะลบ Named Volume ด้วย ซึ่งอาจทำให้ Certificate และข้อมูล TLS ถูกลบ
สำหรับ Production โดยทั่วไปควรใช้
docker compose down
#3. ตรวจสอบ DNS
Domain ต้องชี้มายัง Server ที่ถูกต้อง
nslookup example.com
#4. ตรวจสอบ Port 80 และ 443
sudo ss -tulpn | grep -E ':80|:443'
ถ้ามี Nginx หรือ Apache บน Host ใช้พอร์ตนี้อยู่ Caddy จะไม่สามารถ Bind Port ได้
ตัวอย่างหยุด Nginx:
sudo systemctl stop nginx
sudo systemctl disable nginx
#Production Architecture ที่แนะนำ
Internet
|
v
+--------------+
| Caddy |
| Reverse Proxy|
| TLS / HTTPS |
+------+-------+
|
+--------------+--------------+
| |
v v
+---------------+ +---------------+
| Frontend | | API |
| React / Vue | | Laravel / Go |
+---------------+ +---------------+
| |
+--------------+--------------+
|
Docker Network
Caddy เป็น Entry Point เพียงตัวเดียวจาก Internet ส่วน Application Container ไม่จำเป็นต้องเปิด Port ออกสู่ Public โดยตรง
#ตัวอย่าง Configuration สำหรับ Production
#compose.yaml
services:
web:
image: nginx:alpine
restart: unless-stopped
networks:
- internal
api:
image: my-api:latest
restart: unless-stopped
expose:
- "8000"
networks:
- internal
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- internal
volumes:
caddy_data:
caddy_config:
networks:
internal:
driver: bridge
#Caddyfile
example.com {
encode zstd gzip
header {
X-Content-Type-Options nosniff
X-Frame-Options SAMEORIGIN
Referrer-Policy strict-origin-when-cross-origin
-Server
}
reverse_proxy web:80
log {
output stdout
format json
}
}
api.example.com {
encode zstd gzip
reverse_proxy api:8000
log {
output stdout
format json
}
}
#สรุป
Caddy Server เป็นทางเลือกที่เหมาะสำหรับการทำ Reverse Proxy ในระบบ Docker เนื่องจาก Configuration มีความกระชับและสามารถจัดการ HTTPS ได้อัตโนมัติ
องค์ประกอบสำคัญประกอบด้วย
- Docker Compose สำหรับจัดการ Container
- Caddy สำหรับเป็น Reverse Proxy
- Caddyfile สำหรับกำหนด Routing
- Docker Network สำหรับเชื่อมต่อ Service
- Named Volume สำหรับเก็บ TLS Certificate
- DNS สำหรับชี้ Domain ไปยัง Server
- Firewall สำหรับเปิดพอร์ต HTTP และ HTTPS
Configuration ขั้นต่ำสำหรับ Local Development คือ
:80 {
reverse_proxy web:80
}
ส่วน Production สามารถใช้
example.com {
encode zstd gzip
reverse_proxy web:80
}
เมื่อ DNS ชี้มายัง Server และเปิดพอร์ต 80 กับ 443 ถูกต้อง Caddy จะช่วยจัดการ HTTPS, TLS Certificate และ Redirect จาก HTTP ไป HTTPS ให้อัตโนมัติ
ด้วยเหตุนี้ Caddy + Docker Compose จึงเป็น Stack ที่เหมาะสำหรับการ Deploy Web Application, REST API, Laravel, Node.js, Go, React, Next.js และบริการแบบ Containerized บน VPS หรือ Cloud Server ได้อย่างมีประสิทธิภาพ