Bundle Postgres, Valkey and NATS into the kit image - #252
Merged
07prajwal2000 merged 5 commits intoAug 14, 2026
Conversation
The root dev script still launched @fluxify/web; the portal is the admin UI now. Also binds vite to 0.0.0.0 so Caddy running in a container can reach the dev server through host.docker.internal, and redirects the bare /_/admin/ui path — vite's base only serves the trailing-slash form. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The kit is the trial/demo build, so "copy env.example, edit it, run compose" was the wrong first step. All three backing services now ship in the image and start automatically, making `docker run -p 8080:8080` a complete stack. Each bundled service is skipped when its connection variable is already set (PG_URL / REDIS_HOST / NATS_URL), so docker-compose.yml drives the external topology from the same image with no changes. Also in here, because the kit could not boot without them: - The admin UI is apps/portal now, not the legacy Next.js app. It is a static Vite bundle served directly by Caddy, so the UI process is gone entirely and the build no longer depends on @fluxify/web (which does not typecheck against the current server schemas). - entrypoint.sh supervises properly. A bare `wait` blocks until every child exits, so one dead service left the container "up" and quietly broken — that is how the AI gateway had been crash-looping unnoticed. It now exits non-zero on the first death and lets the restart policy handle it. - DOCS_INDEX_PATH used path.join against an absolute env value, producing /app/ai-gateway/app/ai-gateway/docs-index.bin. path.resolve honours absolute paths and still resolves relative ones against the module directory. - The AI gateway has no schema-wait of its own and died on a missing app_config, so startup is now gated on the admin server answering. - HEALTHCHECK pointed at /_/admin/api/healthchecks/startup, which does not exist; the container never became healthy. Uses public-settings instead. - SEED_USER_EMAIL/PASSWORD are validated before initdb: a fresh database refuses to boot without them rather than coming up with no way to log in. - .gitattributes pins shell scripts to LF. A CRLF shebang makes the kernel look for "/bin/sh\r" and the container dies with "No such file or directory". Verified both paths: bundled boots clean to healthy on a fresh volume, reuses the cluster and generated secrets on restart, and serves the UI, admin API and worker routes; external mode skips all three bundled services. Image is 338MB. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The kit ships its own database, cache and event bus now, so the guide no longer starts at "copy an env file and run compose". It leads with the one-command run and keeps the bring-your-own-services path as the second option, since the image picks between them on its own. Covers the parts that bite: /data must be mounted or everything is discarded, the generated keys in it need backing up, admin credentials are mandatory on a fresh instance, and a bundled major database upgrade is not applied in place. Also documents WORKER_PROJECT_ID=* as a way to skip the create-project-then- restart step, with the path-collision caveat stated plainly. Fixes two dead anchors in the deployments index that pointed at removed sections, and folds the backing services into the kit diagram now that they are inside the container. Production docs deliberately untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two flow editors existed for the same job. The canvas is the real one — block picker, playground, route switcher, settings modal — while the editor page was a thinner duplicate whose non-editor tabs were still "coming soon". Every entry point now goes to the canvas: the Open button on the routes list, the redirect after creating a route, the AI resource chip, and the "Go to route" link on an applied artifact. That last one had a `target` prop to pick between the two destinations; with one destination left it is dead flexibility, so it is gone rather than pinned to a constant. components/editor went with the page — nothing else imported it, the canvas has its own block catalog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both hardcoded /_/admin/ui/public/icons/logo.svg. Files under public/ are served from the bundle root, so the public/ segment resolves to nothing — and because the SPA fallback answers unknown paths with index.html, the request returned 200 with an HTML body instead of 404. The browser got markup where an image was expected and rendered nothing, which is why it looked like a routing fault. Uses BASE_URL like the login and projects pages already do, so the path stays correct if the base ever moves, and switches to webp (18KB). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Why
The kit is the trial build, but getting it running still meant copying an environment file, editing it, and bringing up a four-container compose stack. Caddy was already inside the image, so the backing services were the only reason a compose file was needed at all.
They are now bundled.
docker run -p 8080:8080is a complete Fluxify instance with nothing else installed.What
Bundled backing services. PostgreSQL, Valkey and NATS ship in the image and start automatically. Each one is skipped when its connection variable is already set (
PG_URL/REDIS_HOST/NATS_URL), sodocker/kit/docker-compose.ymldrives the external topology from the same image with no changes. One image, both worlds, no second Dockerfile.State lives in a
/datavolume: the Postgres cluster, the JetStream store, and the encryption/session keys generated on first boot when not supplied. Image is 338MB.Admin credentials are enforced. The seeder skips silently when
SEED_USER_EMAIL/SEED_USER_PASSWORDare missing or malformed, which leaves a running instance nobody can log into. A fresh database now refuses to boot instead, beforeinitdb, with a message saying why.The admin UI is
apps/portal. The kit still built the legacy Next.js app, which no longer typechecks against the current server schemas (it importstest-suites/run/dto, renamed tostart-run). Per AGENT.md that app is legacy and must not be edited, so the kit moved to the shipping UI. It is a static Vite bundle served straight by Caddy, so the UI process is gone entirely.Bugs found on the way
The kit could not boot without these. All predate this branch:
entrypoint.shdid not supervise. A barewaitblocks until every child exits, so one dead service left the container "up" and quietly broken — that is how the AI gateway had been crash-looping unnoticed. It now exits non-zero on the first death and lets the restart policy handle it. Fixing this is what exposed the three below.DOCS_INDEX_PATHusedpath.joinon an absolute env value, producing/app/ai-gateway/app/ai-gateway/docs-index.bin.path.resolvehonours absolute paths and still resolves relative ones against the module directory.app_config. Its startup is now gated on the admin server answering.HEALTHCHECKpointed at/_/admin/api/healthchecks/startup, which does not exist — the container could never report healthy. Usespublic-settings.Also in here
targetprop to choose between the two — dead flexibility now, so removed rather than pinned to a constant.components/editor/went with it; nothing else imported it./_/admin/ui/public/icons/logo.svg; files underpublic/serve from the bundle root, so thepublic/segment matched nothing. Because the SPA fallback answers unknown paths withindex.html, the request returned 200 with an HTML body rather than 404 — the browser got markup where an image was expected. Now usesBASE_URL, and webp..gitattributespins shell scripts to LF. A CRLF shebang makes the kernel look for/bin/sh\r; the container dies withexec /app/entrypoint.sh failed: No such file or directory.devscript launched@fluxify/web; points at@fluxify/portalnow. Vite binds0.0.0.0so a containerised Caddy can reach it viahost.docker.internal, and the bare/_/admin/uipath redirects to the trailing-slash form Vite'sbaseactually serves.Testing
Verified against a running container throughout, not just at the end:
healthyon a fresh volume — migrations, seed user, compiler, worker, AI gateway./_/admin/ui→ 308 → 200.GET /users) returns 200 through the proxy onceWORKER_PROJECT_IDis set.PG_URL/REDIS_HOST/NATS_URLset) starts zero bundled services, so compose is unaffected.apps/portaltypechecks and builds; the route tree regenerates with no editor entries.Not in scope
docker/production/anddocker/admin/are untouched by request. They still buildapps/web, which is worth a follow-up.Docs
docs/deployments/kit.mdleads with the one-command run and keeps bring-your-own-services as the second option. Covers the parts that bite: mount/dataor lose everything, back up the generated keys, admin credentials are mandatory, and a bundled major database upgrade is not applied in place. Also documentsWORKER_PROJECT_ID=*as a way to skip the create-project-then-restart step, with the path-collision caveat stated plainly.Fixes two dead anchors in the deployments index and folds the backing services into the kit diagram.
Related: #251 tracks project-scoped route resolution, which is what would let the kit default to serving every project without the collision caveat.
🤖 Generated with Claude Code