Update - #72
Merged
Merged
Conversation
Dokploy loads exactly one compose file, so the `-f base -f overlay` pattern the rest of the repo uses is unavailable, and three things a Dokploy host needs cannot be expressed from its UI: `web` must publish no host port (Traefik reaches it over `dokploy-network`, and a published port both bypasses the host firewall via DNAT and collides with whatever already holds 3000), every routed service must join that external network, and TLS terminating at Traefik means COOKIE_SECURE has to flip — the same reason docker-compose.caddy.yml flips it. `db` and `api` are pulled in with `extends` rather than copied. That env block is ~60 keys and most of this repo's deployment bugs have been one of them failing to reach the container; a second hand-maintained copy would drift, and the symptom would be "what I configured in Dokploy does nothing". `web` is written out in full instead, because the one thing it must not inherit is `ports:` and Compose merges sequences rather than replacing them. Service names are prefixed, since a Dokploy host shows every project's containers side by side and `api`/`db`/`web` say nothing there. The old names survive as network aliases: `docker/nginx.conf` is baked into the web image and proxies to `api:8000`, and the inherited DATABASE_URL points at `db:5432`, so without the aliases a cosmetic rename would force edits to files the Caddy, cloudflared and dev deployments also use. `depends_on` needs `!override` for the same rename — `extends` carries it over pointing at the base file's names, and re-declaring it merges instead of replacing, leaving a dangling reference that compose refuses to start. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017NQQDUWt5Ga5xTqvjGuSpL
Dokploy parses the compose file and writes it back out before running it — that is how it injects its own network and labels — and the rewrite drops YAML tags. `depends_on: !override` therefore reached Compose as a plain `depends_on`, which MERGES with the one `extends` had copied from docker-compose.yml, so the deploy failed on `service "skillnet-api" depends on undefined service "db"`. None of this is visible locally: there the file reaches Compose untouched and the tag does exactly what it says. So the file no longer relies on anything that has to survive a round-trip: no `extends`, no tags. `db` and `api` are spelled out, `DATABASE_URL` points at `skillnet-postgres` directly (no alias needed for it any more), and only the `api` alias stays, because docker/nginx.conf is baked into the web image and proxies to `api:8000`. The cost is a second copy of `api`'s ~60-key environment block, which is why the header now says in plain words that it has to be kept in sync — the whole class of bug this repo keeps hitting is one env key not reaching the container. The copy was extracted from docker-compose.yml mechanically and diffed against it: same keys, exactly three deliberate value changes (DATABASE_URL host, COOKIE_SECURE and CORS_ORIGINS, the last two for the same reason the Caddy overlay changes them). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017NQQDUWt5Ga5xTqvjGuSpL
The compose file carried its own Traefik labels for ${DOMAIN}. They work, but
adding the domain from Dokploy's Domains tab makes Dokploy write a second set
of labels for the same hostname, and two routers claiming the same `Host()`
rule is a coin flip over which one answers. The UI is where a Dokploy operator
looks for this, so the UI wins and the labels go.
What cannot move to the UI stays: `skillnet-web` still joins the external
`dokploy-network`, because the Domains tab adds labels, not networks, and
Traefik cannot reach a container it shares no network with. Point that tab at
service `skillnet-web`, container port 80 — nginx's port inside the container,
not a host port; there is still deliberately none.
DOMAIN was previously required by the router label. It is still needed, now by
CORS_ORIGINS alone, so the `:?` guard moves there rather than disappearing —
otherwise a missing DOMAIN would have silently produced the origin
`https://`. Setting CORS_ORIGINS explicitly skips the guard, since Compose only
evaluates a default when the variable is unset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017NQQDUWt5Ga5xTqvjGuSpL
On a Dokploy host Traefik is the reverse proxy, so the nginx inside the web image was a second one to keep in sync for nothing. docker/web.dokploy.Dockerfile builds the same Vite bundle and serves it with `serve` on 3000: history-API fallback and headers come from docker/serve.json, which reproduces the three headers docker/security-headers.conf sets plus the immutable/no-store split between /assets and index.html. Dropping nginx moves one job it was doing, and it is not optional: the SPA calls the API on relative paths (`/api/v1/...`, apps/skillnet-web/src/api/client.ts) and has no configurable base URL, so the Domains tab now needs TWO entries on the same host — `/` to skillnet-web:3000 and `/api` to skillnet-api:8000 (plus `/ext` for the external API). Traefik ranks routers by rule length, so the prefix wins over `/` on its own. That is why skillnet-api joins dokploy-network here; the paths it exposes publicly are the ones nginx already forwarded, so it is a shorter route to the same surface, not a wider one. The web healthcheck drops to `/` because `/health` was nginx proxying to the API. Two things this build does differently, both about failures that look like broken code and are not: pnpm is installed from npm instead of `corepack enable`, whose signature check fails opaquely on hosts with an old keyring, and NODE_OPTIONS raises V8's heap ceiling so `tsc -b && vite build` on a small VPS dies with an error rather than a bare "Killed". The existing web.Dockerfile builds fine here (verified), so the Vite step was never the problem. Verified by running the image: index.html, a React Router path, a hashed asset and the headers all come back as intended, serve.json stays out of the served directory, and the container runs as `node`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017NQQDUWt5Ga5xTqvjGuSpL
The header said the Domains tab needs an `/api` entry. It did not say what missing it looks like — `GET /api/v1/setup/status` returning 200 with `filename="index.html"`, which reads like a frontend bug — nor that "Strip Path" has to stay off, since `/api/v1` is where FastAPI mounts its routes rather than a prefix to peel away. Both produce a login that never fires. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017NQQDUWt5Ga5xTqvjGuSpL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
montón de cosas a configur
Checks
uv run ruff check src testsanduv run pytest -m "not integration"(fromapps/skillnet-api/)pnpm lint,pnpm testandpnpm build(fromapps/skillnet-web/).env.example? A cleandocker compose up -d --buildstill comes up and/api/v1/healthis green.Notes for the reviewer