docker-compose.prod.yml built everything from source and never used the images published by images.yml. Worse, the frontend baked VITE_API_URL into the JS bundle at build time, so the published flowsint-app image pointed at whatever URL CI had at build — "fail to fetch" for any other deployment. A pre-built image with a hardcoded API URL is a structural contradiction.
Fix
Same-origin by design:
VITE_API_URL defaults to "" → bundle calls relative /api/... URLs (all API routes already carry the /api prefix)
Frontend nginx reverse-proxies /api/ to the api service over the Docker network (request-time DNS resolution, SSE support via proxy_buffering off)
CORS becomes irrelevant: front and API share one origin
?? '' guard on import.meta.env.VITE_API_URL kills the undefined/api/... bug class
Image-based compose:
docker-compose.prod.yml uses ghcr.io/reconurge/flowsint-{app,api}:${FLOWSINT_VERSION:-latest}, no build: sections
postgres / redis / neo4j / api ports bound to 127.0.0.1 — only 5173 faces the network (default redis has no password, postgres ships default creds)
Housekeeping:
Makefile: make prod pulls instead of building; removed dead deploy targets referencing nonexistent docker-compose.deploy.yml
README: "Deploy on a network (team / server)" section — secrets checklist, version pinning, TLS via reverse proxy
Full stack ran locally (app built from this branch + api from ghcr): all healthchecks green, GET /api/types through the proxy → FastAPI 401, POST /api/auth/token → FastAPI 422 validation (method + body traverse), SSE route reachable, no host baked in the bundle ✓
Note
Requires a new tag (v1.2.10) after merge: current ghcr :latest frontend predates this fix and still ships the broken baked-URL bundle.
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/reconurge/flowsint/pull/175
**Author:** [@dextmorgn](https://github.com/dextmorgn)
**Created:** 6/5/2026
**Status:** ✅ Merged
**Merged:** 6/5/2026
**Merged by:** [@dextmorgn](https://github.com/dextmorgn)
**Base:** `main` ← **Head:** `fix/prebuilt-images-compose`
---
### 📝 Commits (1)
- [`82925be`](https://github.com/reconurge/flowsint/commit/82925bec38f9be0ad47a776bcb37dd79259907ed) fix(deploy): serve prod from pre-built images
### 📊 Changes
**10 files changed** (+96 additions, -70 deletions)
<details>
<summary>View changed files</summary>
📝 `.env.example` (+3 -1)
📝 `Makefile` (+14 -43)
📝 `README.md` (+38 -2)
📝 `docker-compose.prod.yml` (+14 -18)
📝 `flowsint-app/Dockerfile` (+3 -2)
📝 `flowsint-app/nginx.conf` (+20 -0)
📝 `flowsint-app/src/api/api.ts` (+1 -1)
📝 `flowsint-app/src/api/chat-transport.ts` (+1 -1)
📝 `flowsint-app/src/hooks/use-events.ts` (+1 -1)
📝 `flowsint-app/src/hooks/use-graph-refresh.ts` (+1 -1)
</details>
### 📄 Description
## Problem
`docker-compose.prod.yml` built everything from source and never used the images published by `images.yml`. Worse, the frontend baked `VITE_API_URL` into the JS bundle at build time, so the published `flowsint-app` image pointed at whatever URL CI had at build — **"fail to fetch"** for any other deployment. A pre-built image with a hardcoded API URL is a structural contradiction.
## Fix
**Same-origin by design:**
- `VITE_API_URL` defaults to `""` → bundle calls relative `/api/...` URLs (all API routes already carry the `/api` prefix)
- Frontend nginx reverse-proxies `/api/` to the `api` service over the Docker network (request-time DNS resolution, SSE support via `proxy_buffering off`)
- CORS becomes irrelevant: front and API share one origin
- `?? ''` guard on `import.meta.env.VITE_API_URL` kills the `undefined/api/...` bug class
**Image-based compose:**
- `docker-compose.prod.yml` uses `ghcr.io/reconurge/flowsint-{app,api}:${FLOWSINT_VERSION:-latest}`, no `build:` sections
- postgres / redis / neo4j / api ports bound to `127.0.0.1` — only `5173` faces the network (default redis has no password, postgres ships default creds)
**Housekeeping:**
- Makefile: `make prod` pulls instead of building; removed dead `deploy` targets referencing nonexistent `docker-compose.deploy.yml`
- README: "Deploy on a network (team / server)" section — secrets checklist, version pinning, TLS via reverse proxy
## Verification
- `docker compose -f docker-compose.prod.yml config` ✓
- `nginx -t` on the new config ✓
- Full stack ran locally (app built from this branch + api from ghcr): all healthchecks green, `GET /api/types` through the proxy → FastAPI `401`, `POST /api/auth/token` → FastAPI `422` validation (method + body traverse), SSE route reachable, no host baked in the bundle ✓
## Note
Requires a new tag (`v1.2.10`) after merge: current ghcr `:latest` frontend predates this fix and still ships the broken baked-URL bundle.
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/reconurge/flowsint/pull/175
Author: @dextmorgn
Created: 6/5/2026
Status: ✅ Merged
Merged: 6/5/2026
Merged by: @dextmorgn
Base:
main← Head:fix/prebuilt-images-compose📝 Commits (1)
82925befix(deploy): serve prod from pre-built images📊 Changes
10 files changed (+96 additions, -70 deletions)
View changed files
📝
.env.example(+3 -1)📝
Makefile(+14 -43)📝
README.md(+38 -2)📝
docker-compose.prod.yml(+14 -18)📝
flowsint-app/Dockerfile(+3 -2)📝
flowsint-app/nginx.conf(+20 -0)📝
flowsint-app/src/api/api.ts(+1 -1)📝
flowsint-app/src/api/chat-transport.ts(+1 -1)📝
flowsint-app/src/hooks/use-events.ts(+1 -1)📝
flowsint-app/src/hooks/use-graph-refresh.ts(+1 -1)📄 Description
Problem
docker-compose.prod.ymlbuilt everything from source and never used the images published byimages.yml. Worse, the frontend bakedVITE_API_URLinto the JS bundle at build time, so the publishedflowsint-appimage pointed at whatever URL CI had at build — "fail to fetch" for any other deployment. A pre-built image with a hardcoded API URL is a structural contradiction.Fix
Same-origin by design:
VITE_API_URLdefaults to""→ bundle calls relative/api/...URLs (all API routes already carry the/apiprefix)/api/to theapiservice over the Docker network (request-time DNS resolution, SSE support viaproxy_buffering off)?? ''guard onimport.meta.env.VITE_API_URLkills theundefined/api/...bug classImage-based compose:
docker-compose.prod.ymlusesghcr.io/reconurge/flowsint-{app,api}:${FLOWSINT_VERSION:-latest}, nobuild:sections127.0.0.1— only5173faces the network (default redis has no password, postgres ships default creds)Housekeeping:
make prodpulls instead of building; removed deaddeploytargets referencing nonexistentdocker-compose.deploy.ymlVerification
docker compose -f docker-compose.prod.yml config✓nginx -ton the new config ✓GET /api/typesthrough the proxy → FastAPI401,POST /api/auth/token→ FastAPI422validation (method + body traverse), SSE route reachable, no host baked in the bundle ✓Note
Requires a new tag (
v1.2.10) after merge: current ghcr:latestfrontend predates this fix and still ships the broken baked-URL bundle.🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.