diff --git a/README.md b/README.md index d32a7ea..0eb468f 100644 --- a/README.md +++ b/README.md @@ -218,9 +218,10 @@ pattern, where to start contributing). `docker-compose.microservices.yml` starts the new gateway, identity, rooms, and notify service hosts plus Compose-managed Postgres and Redis for local testing. `docker-compose.microservices.dokploy.yml` is the -Dokploy version; it reads `DORKNET_DOMAIN` and publishes the required -Traefik host rules for the gateway. These files do **not** replace the -full `DorkNet.Server` runtime yet; the public API is still served by the +Dokploy version; it adds a `cloudflared` sidecar so Cloudflare Tunnel +can route the apex and wildcard hostnames to the gateway without +Dokploy domain rows. These files do **not** replace the full +`DorkNet.Server` runtime yet; the public API is still served by the monolith while domains are peeled into services incrementally. The compose file intentionally does not start object storage. Point it at diff --git a/docker-compose.microservices.dokploy.yml b/docker-compose.microservices.dokploy.yml index 3aee82a..f3e0457 100644 --- a/docker-compose.microservices.dokploy.yml +++ b/docker-compose.microservices.dokploy.yml @@ -14,60 +14,18 @@ services: DorkNet__Services__Monolith: "" expose: - "8080" - labels: - traefik.enable: "true" - traefik.docker.network: dokploy-network - traefik.http.routers.dorknet-gateway.entrypoints: web - traefik.http.routers.dorknet-gateway.service: dorknet-gateway - traefik.http.routers.dorknet-gateway.rule: >- - Host(`${DORKNET_DOMAIN}`) - || Host(`www.${DORKNET_DOMAIN}`) - || Host(`admin.${DORKNET_DOMAIN}`) - || Host(`api.${DORKNET_DOMAIN}`) - || Host(`auth.${DORKNET_DOMAIN}`) - || Host(`accounts.${DORKNET_DOMAIN}`) - || Host(`bugreporting.${DORKNET_DOMAIN}`) - || Host(`cards.${DORKNET_DOMAIN}`) - || Host(`cdn.${DORKNET_DOMAIN}`) - || Host(`chat.${DORKNET_DOMAIN}`) - || Host(`clubs.${DORKNET_DOMAIN}`) - || Host(`cms.${DORKNET_DOMAIN}`) - || Host(`commerce.${DORKNET_DOMAIN}`) - || Host(`data.${DORKNET_DOMAIN}`) - || Host(`datacollection.${DORKNET_DOMAIN}`) - || Host(`discovery.${DORKNET_DOMAIN}`) - || Host(`econ.${DORKNET_DOMAIN}`) - || Host(`feed.${DORKNET_DOMAIN}`) - || Host(`gamelogs.${DORKNET_DOMAIN}`) - || Host(`geo.${DORKNET_DOMAIN}`) - || Host(`img.${DORKNET_DOMAIN}`) - || Host(`leaderboard.${DORKNET_DOMAIN}`) - || Host(`link.${DORKNET_DOMAIN}`) - || Host(`lists.${DORKNET_DOMAIN}`) - || Host(`match.${DORKNET_DOMAIN}`) - || Host(`moderation.${DORKNET_DOMAIN}`) - || Host(`notify.${DORKNET_DOMAIN}`) - || Host(`ns.${DORKNET_DOMAIN}`) - || Host(`platformnotifications.${DORKNET_DOMAIN}`) - || Host(`playersettings.${DORKNET_DOMAIN}`) - || Host(`roomcomments.${DORKNET_DOMAIN}`) - || Host(`roomieintegrations.${DORKNET_DOMAIN}`) - || Host(`rooms.${DORKNET_DOMAIN}`) - || Host(`storage.${DORKNET_DOMAIN}`) - || Host(`strings.${DORKNET_DOMAIN}`) - || Host(`strings-cdn.${DORKNET_DOMAIN}`) - || Host(`studio.${DORKNET_DOMAIN}`) - || Host(`thorn.${DORKNET_DOMAIN}`) - || Host(`videos.${DORKNET_DOMAIN}`) - traefik.http.services.dorknet-gateway.loadbalancer.server.port: "8080" - networks: - - default - - dokploy-network depends_on: - identity - rooms - notify + cloudflared: + image: cloudflare/cloudflared:latest + command: tunnel --no-autoupdate run --token ${CLOUDFLARE_TUNNEL_TOKEN:?Set CLOUDFLARE_TUNNEL_TOKEN in Dokploy} + depends_on: + - gateway + restart: unless-stopped + identity: build: context: . @@ -117,9 +75,5 @@ services: redis: image: redis:7-alpine -networks: - dokploy-network: - external: true - volumes: microservices-postgres: diff --git a/docs/architecture.md b/docs/architecture.md index 7a35c04..b8b2fe8 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -84,13 +84,13 @@ capability endpoints today: `docker-compose.microservices.yml` starts gateway, identity, rooms, and notify alongside Compose-managed Postgres and Redis for local testing. -`docker-compose.microservices.dokploy.yml` is the Dokploy shape: it puts -the gateway on `dokploy-network` and builds exact Traefik host rules from -`DORKNET_DOMAIN`. Object storage is external S3-compatible storage -configured with `DORKNET_S3_*` environment variables; the compose files -do not run MinIO or Garage. The next migration step is to move one public -route group at a time behind the gateway while preserving existing URLs -and response shapes. +`docker-compose.microservices.dokploy.yml` is the Dokploy shape: it adds +a `cloudflared` sidecar that joins the same Compose network and forwards +Cloudflare Tunnel traffic to `http://gateway:8080`. Object storage is +external S3-compatible storage configured with `DORKNET_S3_*` +environment variables; the compose files do not run MinIO or Garage. The +next migration step is to move one public route group at a time behind +the gateway while preserving existing URLs and response shapes. ### Controllers/ groupings diff --git a/docs/deploy.md b/docs/deploy.md index 5ee8cf8..4d3aecb 100644 --- a/docs/deploy.md +++ b/docs/deploy.md @@ -129,8 +129,8 @@ Create a Dokploy **Compose** service from one of these files: | Compose file | Use | |---|---| -| `docker-compose.microservices.yml` | Local/dev-style compose; use Dokploy's domain UI manually | -| `docker-compose.microservices.dokploy.yml` | Dokploy Traefik labels generated from `DORKNET_DOMAIN` | +| `docker-compose.microservices.yml` | Local/dev-style compose | +| `docker-compose.microservices.dokploy.yml` | Dokploy compose with a Cloudflare Tunnel sidecar | Both compose files provide: @@ -142,50 +142,34 @@ Both compose files provide: | `notify` | Future notification/presence service | | `postgres` | Internal Postgres for the service network | | `redis` | Internal Redis for ephemeral state and fan-out | +| `cloudflared` | Cloudflare Tunnel sidecar; Dokploy file only | -Only the gateway needs a public domain during scaffold testing. Route the -gateway domain to service `gateway`, container port `8080`. +### Cloudflare Tunnel domains -In Dokploy's Compose domain modal: +Use `docker-compose.microservices.dokploy.yml` with Cloudflare Tunnel. +Do not add Dokploy domain rows for this compose stack; public traffic +enters through the `cloudflared` sidecar instead of Dokploy's Traefik +router. -| Field | Value | -|---|---| -| Service Name | `gateway` | -| Host | the domain you are adding | -| Path | `/` | -| Internal Path | `/` | -| Container Port | `8080` | -| Strip Path | off | - -If the service dropdown says **Services not found**, deploy or redeploy -the Compose service first, then refresh the domain modal. Dokploy derives -the service list from the parsed compose file. - -### Env-driven domains - -Use `docker-compose.microservices.dokploy.yml` if you do not want to add -every domain row manually in Dokploy. Set: +Set these Dokploy Compose environment variables: ```env DORKNET_DOMAIN=yourdomain.com +CLOUDFLARE_TUNNEL_TOKEN= ``` -That file adds Traefik labels to `gateway` for the apex plus every host -listed in the full-server domain list above, then sends them all to -container port `8080`. It also attaches the gateway to Dokploy's external -`dokploy-network`, which is why this is a separate file instead of the -default local compose file. - -DNS still has to resolve to the Dokploy server. The simplest setup is an -apex record plus wildcard subdomain record: +In Cloudflare Zero Trust, configure the tunnel with two public hostnames: ```text -yourdomain.com A/AAAA -*.yourdomain.com A/AAAA +yourdomain.com -> http://gateway:8080 +*.yourdomain.com -> http://gateway:8080 ``` -If you do not use wildcard DNS, add A/AAAA or CNAME records for each -listed subdomain. +The wildcard covers `api`, `auth`, `rooms`, `notify`, and the other +client-facing subdomains. The apex rule is separate because wildcard DNS +does not cover the root domain. Let Cloudflare create the tunnel DNS +records, or create CNAME records to the tunnel target Cloudflare gives +you. When the gateway starts carrying the public API, every public DorkNet domain listed above should point to `gateway`. Until then, keep the real @@ -207,9 +191,9 @@ service unless you intentionally want a local dev-only S3 emulator. Useful smoke checks after deploy: ```bash -curl https://gateway.yourdomain.com/healthz -curl https://gateway.yourdomain.com/internal/services -curl https://gateway.yourdomain.com/internal/services/health +curl https://api.yourdomain.com/healthz +curl https://api.yourdomain.com/internal/services +curl https://api.yourdomain.com/internal/services/health ``` Expected during scaffold-only deploys: `/internal/services/health` diff --git a/tools/cloudflared.README.md b/tools/cloudflared.README.md index de0ecfb..8f219c5 100644 --- a/tools/cloudflared.README.md +++ b/tools/cloudflared.README.md @@ -59,6 +59,27 @@ Or install as a Windows service so it survives reboots: cloudflared service install ``` +## Dokploy sidecar + +For Dokploy microservices, use +`docker-compose.microservices.dokploy.yml`. It starts `cloudflared` in +the same Compose network as `gateway`, so the tunnel origin is: + +```text +http://gateway:8080 +``` + +Set `CLOUDFLARE_TUNNEL_TOKEN` in Dokploy from the Cloudflare Zero Trust +tunnel token. In the Cloudflare tunnel's public hostnames, add: + +```text +yourdomain.com -> http://gateway:8080 +*.yourdomain.com -> http://gateway:8080 +``` + +Do not add these domains in Dokploy's domain modal for this compose +stack; Cloudflare owns the public hostname routing. + ## What happens at request time ```