diff --git a/docs/setup.md b/docs/setup.md index 7bc37ae..4100634 100644 --- a/docs/setup.md +++ b/docs/setup.md @@ -140,29 +140,24 @@ docker run --rm hello-world # verify --- -## 6. Domain and DNS (Cloudflare) +## 6. Domain and DNS -You need a domain whose DNS is managed by Cloudflare (the free plan is fine). +You need a domain pointing to your Pi's public IP. Any DNS provider works. -1. In Cloudflare, add two DNS records pointing to your **public home IP**: +1. Add DNS A records pointing to your **public home IP**: - | Type | Name | Content | Proxy | - |---|---|---|---| - | A | `hiy` | `` | Proxied (orange cloud) | - | A | `*` | `` | Proxied (orange cloud) | + | Type | Name | Content | + |---|---|---| + | A | `hiy` | `` | + | A | `*` | `` | The wildcard `*` record routes `.yourdomain.com` to the Pi. -2. In **Cloudflare → SSL/TLS → Overview**, set mode to **Full (strict)**. - -3. Create a **Cloudflare API token** for Caddy's ACME DNS challenge: - - Go to My Profile → API Tokens → Create Token - - Use the **Edit zone DNS** template, scope it to your zone - - Copy the token — you will need it in step 8 - ### Port forwarding -Forward the following ports on your router to the Pi's static IP: +Forward the following ports on your router to the Pi's static IP. +**Both ports are required** — Caddy uses port 80 to prove domain ownership +to Let's Encrypt before issuing the HTTPS certificate. | External | Internal | |---|---| @@ -195,63 +190,20 @@ Minimum required variables: # Your domain (apps will be at .yourdomain.com) DOMAIN_SUFFIX=yourdomain.com -# Cloudflare API token for ACME DNS-01 challenge -CF_API_TOKEN=your_cloudflare_api_token +# Email for Let's Encrypt expiry notices +ACME_EMAIL=you@example.com ``` --- ## 9. Caddy — automatic HTTPS -Caddy handles TLS via Cloudflare's DNS challenge (no port-80 HTTP challenge -needed, works even behind CGNAT). +Caddy obtains a Let's Encrypt certificate automatically via the HTTP-01 +challenge. No DNS API token or Cloudflare account required. -Edit `proxy/caddy.json` and replace the top-level config with: - -```json -{ - "admin": { "listen": "0.0.0.0:2019" }, - "apps": { - "tls": { - "automation": { - "policies": [{ - "subjects": ["*.yourdomain.com", "yourdomain.com"], - "issuers": [{ - "module": "acme", - "challenges": { - "dns": { - "provider": { - "name": "cloudflare", - "api_token": "{env.CF_API_TOKEN}" - } - } - } - }] - }] - } - }, - "http": { - "servers": { - "hiy": { - "listen": [":80", ":443"], - "routes": [ - { - "handle": [{ - "handler": "reverse_proxy", - "upstreams": [{"dial": "server:3000"}] - }] - } - ] - } - } - } - } -} -``` - -> **Note:** The Caddy image in `docker-compose.yml` needs the Cloudflare DNS -> plugin. Use `caddy:2-alpine` with `xcaddy` or replace the image with -> `ghcr.io/caddybuilds/caddy-cloudflare:latest`. +The `proxy/Caddyfile` is already configured — nothing to edit here. +Just make sure `DOMAIN_SUFFIX` and `ACME_EMAIL` are set in `infra/.env` +and that ports 80 and 443 are forwarded to the Pi (see step 6). --- @@ -427,7 +379,7 @@ Caddy never restarts. | Symptom | Likely cause | Fix | |---|---|---| | Dashboard not reachable | Compose not started / port 443 not forwarded | `docker compose logs server` | -| TLS certificate error | CF_API_TOKEN wrong or DNS not propagated | Check Caddy logs: `docker compose logs caddy` | +| TLS certificate error | Port 80/443 not forwarded or DNS not propagated yet | Check Caddy logs: `docker compose logs caddy` | | Build fails — "docker not found" | Docker socket not mounted | Verify `docker-proxy` service is up | | Build fails — "pack not found" | `pack` not installed | See step 11 | | Webhook returns 401 | Secret mismatch | Regenerate app, re-copy secret to GitHub | diff --git a/infra/.env.example b/infra/.env.example new file mode 100644 index 0000000..8ff5295 --- /dev/null +++ b/infra/.env.example @@ -0,0 +1,5 @@ +# Your domain — apps will be served at .yourdomain.com +DOMAIN_SUFFIX=yourdomain.com + +# Email address for Let's Encrypt expiry notices (required for HTTPS) +ACME_EMAIL=you@example.com diff --git a/infra/docker-compose.yml b/infra/docker-compose.yml index c4c257a..c041ec8 100644 --- a/infra/docker-compose.yml +++ b/infra/docker-compose.yml @@ -53,11 +53,14 @@ services: - "80:80" - "443:443" - "2019:2019" # admin API + environment: + DOMAIN_SUFFIX: ${DOMAIN_SUFFIX:-localhost} + ACME_EMAIL: ${ACME_EMAIL:-} volumes: - - ../proxy/caddy.json:/etc/caddy/caddy.json:ro + - ../proxy/Caddyfile:/etc/caddy/Caddyfile:ro - caddy-data:/data - caddy-config:/config - command: caddy run --config /etc/caddy/caddy.json + command: caddy run --config /etc/caddy/Caddyfile --adapter caddyfile networks: - hiy-net - default diff --git a/proxy/Caddyfile b/proxy/Caddyfile index e1d857a..322af1c 100644 --- a/proxy/Caddyfile +++ b/proxy/Caddyfile @@ -1,29 +1,32 @@ # HIY — Caddyfile # -# Local development: auto-HTTPS is disabled; everything runs on :80. -# Production (Pi): remove the `auto_https off` line, set your real email, -# and Caddy will obtain Let's Encrypt certificates automatically. +# Caddy automatically obtains a Let's Encrypt certificate for every domain it +# serves (HTTP-01 challenge). No Cloudflare or DNS API token required. # -# Wildcard TLS on the Pi (recommended): -# tls your@email.com { -# dns cloudflare {env.CLOUDFLARE_API_TOKEN} -# } +# Requirements: +# - Ports 80 and 443 must be publicly reachable (router port-forward to Pi) +# - DNS A record for {$DOMAIN_SUFFIX} must point to your public IP +# - Set ACME_EMAIL in infra/.env (Let's Encrypt needs a contact address) +# +# Local dev: set DOMAIN_SUFFIX=localhost in infra/.env — Caddy will use a +# self-signed cert automatically for localhost. { - # Admin API — used by build.sh to update routes dynamically. + # Admin API — used by hiy-server to add/remove app routes dynamically. admin 0.0.0.0:2019 - # Comment this out on the Pi with a real domain. - auto_https off + # Contact email for Let's Encrypt expiry notices. + email {$ACME_EMAIL} } -# Fallback: serves the HIY dashboard itself. -:80 { +# HIY dashboard — served at your root domain. +{$DOMAIN_SUFFIX} { reverse_proxy server:3000 } -# Example of what the build script adds via the Caddy API: +# Deployed apps are added here dynamically by hiy-server via the Caddy API. +# Each entry looks like: # -# myapp.yourdomain.com { -# reverse_proxy :3000 +# myapp.{$DOMAIN_SUFFIX} { +# reverse_proxy : # }