operations/caddy-dev-setup.md

Cài Caddy trên Dev Server (native, không Docker)

Dev server cài thủ công (api/web chạy bằng yarn, không Docker). Hướng dẫn này cài Caddy native lên host để test custom domain giống production — auto-HTTPS Let's Encrypt thật + on-demand TLS.

Production thì khác: Caddy chạy trong Docker (docker-compose.prod.yml + caddy/Caddyfile) — xem docker-deploy.md §2.4. Đừng lẫn 2 cái.

Điều kiện: dev server có IP public, port 80 + 443 mở ra internet (Caddy cần port 80 cho ACME http-01). api chạy ở 127.0.0.1:3010, web ở 127.0.0.1:3020.


1. Cài Caddy (Ubuntu/Debian, apt repo chính thức)

sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy

Gói này tự tạo systemd service caddy.service đọc /etc/caddy/Caddyfile, chạy dưới user caddy, data ở /var/lib/caddy/.local/share/caddy (cert store ở đây).

caddy version          # xác nhận đã cài
systemctl status caddy # service có sẵn (đang chạy với Caddyfile mặc định)

2. Đặt Caddyfile

Copy caddy/Caddyfile.dev từ repo lên server, sửa dev.glamvoo.com thành dev platform domain thật của bạn (domain trỏ về IP dev server):

sudo cp /path/to/booking-system/caddy/Caddyfile.dev /etc/caddy/Caddyfile
sudo nano /etc/caddy/Caddyfile     # đổi dev.glamvoo.com → domain của bạn

Caddyfile.dev dùng upstream 127.0.0.1:3010 (api) + 127.0.0.1:3020 (web) — khớp với api/web chạy yarn trên host.


3. Inject env (ACME_EMAIL) qua systemd override

sudo systemctl edit caddy

Thêm:

[Service]
Environment=ACME_EMAIL=dev@glamvoo.com

Lưu → reload:

sudo systemctl restart caddy

{$ACME_EMAIL} trong Caddyfile đọc từ env của process Caddy. systemd override là cách chuẩn để set env cho gói Caddy.


4. Mở firewall + dừng thứ chiếm port 80/443

sudo ufw allow 80,443/tcp        # nếu dùng ufw

# Nếu trước đó có nginx/apache native chiếm 80/443 → dừng:
sudo systemctl disable --now nginx 2>/dev/null || true
sudo systemctl disable --now apache2 2>/dev/null || true
sudo lsof -i :443                # xác nhận chỉ còn caddy

Nếu dev đang dùng Cloudflare tunnel cho app-dev.novagoo.com: tunnel không chiếm 80/443 trên host (nó là outbound), nên không xung đột. Caddy phục vụ dev platform domain + custom domain qua đường public IP trực tiếp, song song với tunnel.


5. Cấu hình api + web cho dev domain

Trong .env của api (dev):

PLATFORM_DOMAINS=dev.glamvoo.com          # + domain test KHÁC custom domain
PUBLIC_WEB_URL=https://dev.glamvoo.com
# Chỉ cần nếu test APEX custom domain — IP public của dev server:
CUSTOM_DOMAIN_SERVER_IPS=<IP-dev-server>
# Đích CNAME mà custom domain trỏ tới (nếu test subdomain). Có thể trỏ
# thẳng A record về IP dev cũng được — xem bước 6.
CUSTOM_DOMAIN_CNAME_TARGET=dev.glamvoo.com

Trong env của web (dev):

NEXT_PUBLIC_PLATFORM_DOMAINS=dev.glamvoo.com
# Nếu chạy `yarn dev` (next dev): cho host custom-domain test qua HMR check
DEV_ORIGINS=salon-test.example.com

Restart api + web (yarn start:dev / yarn dev / yarn start) để nạp env mới.

NEXT_PUBLIC_PLATFORM_DOMAINS quyết định proxy.ts coi host nào là platform (không rewrite). Custom domain test KHÔNG nằm trong list này → mới được rewrite về /b/<slug>.


6. Test custom domain trên dev

  1. DNS: trỏ host test về IP dev server. Khuyến nghị subdomain (dễ nhất):
    • salon-test.example.com CNAME dev.glamvoo.com (hoặc A record → IP dev).
    • _glamvoo-verify.salon-test.example.com TXT <token> (token hiện ở admin sau khi add).
  2. Admin: đăng nhập owner → Settings → Domain → thêm salon-test.example.com → copy DNS → Verify → status ACTIVE.
  3. Mở https://salon-test.example.com/ → Caddy ask hỏi api /internal/tls-allow → 200 (vì ACTIVE) → cấp cert Let's Encrypt thật → trang salon hiện, URL sạch.

7. Verify + debug

sudo systemctl status caddy
sudo journalctl -u caddy -f                      # log realtime — tìm "certificate obtained"
curl -I https://dev.glamvoo.com                   # platform domain OK
curl "http://127.0.0.1:3010/api/internal/tls-allow?domain=salon-test.example.com"  # 200 nếu ACTIVE

# Reload Caddyfile sau khi sửa (zero-downtime, không mất cert)
sudo systemctl reload caddy

Failure modes: xem troubleshooting.md §Custom domain. Hay gặp trên dev:

  • Cert không cấp → domain chưa ACTIVE (ask trả 404) hoặc port 80 bị chặn.
  • Mở custom domain ra /error-404NEXT_PUBLIC_PLATFORM_DOMAINS lỡ chứa host đó, hoặc cache proxy 60s chưa hết sau verify.

Liên quan