# Reverse proxy template for a REAL deployment (real domain + automatic HTTPS). # Not needed for local testing against localhost:4650 — this is the step you # take once you're actually putting this in front of a public domain. # # Our recommendations where to deploy this streamable online course and training platform # is only either Cloudflare (with Tunnels For free) or other cloud VPS platforms (DigitalOcean,Google et.al) # # Usage: # 1. Point 3 DNS records at this machine: your app domain, your api # subdomain, and your cdn subdomain (matching FRONTEND_URL / APP_URL / # S3_PUBLIC_URL in apps/api/.env and VITE_APP_URL / VITE_API_URL in # apps/web/.env). Either edit the three domains below directly, or set # the FRONTEND_DOMAIN / API_DOMAIN / CDN_DOMAIN env vars before running # Caddy and leave the placeholders as-is. # 2. Install Caddy (https://caddyserver.com/docs/install) directly on this # host — it is NOT part of docker-compose.yml, so it binds host ports # 80/443 directly and reverse-proxies to the containers' published # ports below. # 3. Copy this file to /etc/caddy/Caddyfile (or wherever your install # expects it). # 4. sudo systemctl reload caddy (or: caddy run --config Caddyfile) # # Caddy issues and renews Let's Encrypt certificates automatically the first # time each domain is requested — no manual cert steps needed. { servers { trusted_proxies static private_ranges client_ip_headers X-Forwarded-For X-Real-IP } } {$FRONTEND_DOMAIN:yourdomain.com} { encode gzip zstd reverse_proxy localhost:4650 { header_up X-Real-IP {http.request.header.X-Real-IP} } handle_errors { respond "Frontend temporarily unavailable" 502 } } {$API_DOMAIN:api.yourdomain.com} { encode gzip zstd reverse_proxy localhost:3024 { header_up X-Real-IP {http.request.header.X-Real-IP} } handle_errors { respond "API temporarily unavailable" 502 } } {$CDN_DOMAIN:cdn.yourdomain.com} { # Browser-direct presigned PUT uploads need a storage CORS preflight. # Garage's default bucket policy only advertises GET, so handle the # browser preflight at the public edge and expose multipart ETags. @storage_preflight method OPTIONS header @storage_preflight { Access-Control-Allow-Origin "{http.request.header.Origin}" Access-Control-Allow-Methods "GET, HEAD, PUT, POST, DELETE, OPTIONS" Access-Control-Allow-Headers "{http.request.header.Access-Control-Request-Headers}" Access-Control-Max-Age "86400" } respond @storage_preflight 204 header { Access-Control-Allow-Origin "*" Access-Control-Expose-Headers "ETag, Content-Length, Content-Range, Accept-Ranges, Content-Disposition" } reverse_proxy localhost:3900 { header_up X-Real-IP {http.request.header.X-Real-IP} header_down -Access-Control-Allow-Origin header_down -Access-Control-Allow-Methods header_down -Access-Control-Allow-Headers header_down -Access-Control-Expose-Headers } }