fix(frontend): keep the config template outside the web root so a restart can't crash-loop - #1400
Merged
iammukeshm merged 3 commits intoSep 28, 2026
Conversation
…tart can't crash-loop The entrypoints deleted config.json.template after rendering it, but the container's writable layer survives docker restart, daemon restarts and reboots, so the next start found no template and set -e exited 1 forever. Keeping the template in /etc/fsh means it is never served and every start re-renders config.json idempotently.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
config.json.template and nginx.conf fell under text=auto, so an image built from a Windows checkout served config.json with CRLF line endings.
Nothing built or ran the nginx images, which is how an entrypoint that deleted its own template shipped: the first start worked and every restart crash-looped. The new job starts each image, checks the rendered config.json, confirms the template is not served, restarts, and checks again.
iammukeshm
approved these changes
Sep 28, 2026
iammukeshm
left a comment
Member
There was a problem hiding this comment.
Right fix: the template belongs outside the web root, and re-rendering on every start is what a restart should do. The container smoke job closes the gap that let this ship (nothing built or restarted the nginx images). The LF rule is a good catch too. Thanks, Marcelo.
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.
Fixes #1399.
The admin and dashboard entrypoints deleted
config.json.templateafter renderingconfig.jsonfrom it, so that the template would not be served. The container's writable layer survives a restart, which means the second start found no template, andset -eturned that into a permanent crash loop.The template now lives at
/etc/fsh/config.json.template, outside the nginx root (/usr/share/nginx/html). It cannot be served from there, so nothing needs deleting, and every start re-rendersconfig.jsonfrom it. Rendering on each start is idempotent. It is also what a restart should do anyway.Four files change (+8/-10): the two
Dockerfiles copy the template to the new path, and the two entrypoints read it from there and no longerrmit. The admin files keep their WHY-comments, updated to stay true. Nothing else in this repo references the old path:nginx.conf, compose, Terraform (which shipsconfig.jsonthrough S3), dokploy and the AppHost do not touch it.Two follow-ups from review are also on this branch:
fcf18d30).config.json.templateandnginx.conffell under* text=auto, so an image built from a Windows checkout servedconfig.jsonwith CRLF line endings. One.gitattributesrule (clients/*/docker/** text eol=lf) now covers every file copied into the front-end images. The index was already LF, so no file content changes.804ab79e). No workflow built or ran the nginx images, which is how this bug shipped: the first start worked and every restart crash-looped. The newContainer (admin|dashboard)job in Frontend CI builds each image and starts it. It then checks the renderedconfig.jsonwithjqand confirms the template is not served. Last, it runsdocker restartand repeats the checks. TheFrontend CIgate now depends on it.Verification
I built both images from this branch and ran them with
--restart unless-stopped, the same way the compose stack does. All 20 steps passed:GET /config.jsonreturns the injectedapiBase,defaultTenantand (admin)dashboardUrl, parsed as JSON rather than eyeballed.GET /config.json.templatereturns the SPA'sindex.htmlfallback, with no${FSH_API_URL}placeholder anywhere in the body.docker restart: the container is still running with a restart count of 0, andconfig.jsonstill has the right values.docker stop+docker start: same result.FSH_API_URL, orFSH_DASHBOARD_URLfor admin, still fails fast with exit code 2 and the existingis requiredmessage.main: the same restart step fails on images built frommain. Both go toexit 1withcan't open /usr/share/nginx/html/config.json.template: no such file, so the check is capable of going red.I also ran the new CI step locally, extracting the step verbatim from the workflow. It passes on this branch for both apps. On images built from
mainit fails at the right point: the first check passes, and the check afterdocker restartfails. On this branch, built from a Windows checkout, the servedconfig.jsonhas 0 CR characters. The same template onmainhas 5 (admin) and 4 (dashboard).It merges cleanly with #1383, which edits the admin entrypoint and template to add
FSH_DEFAULT_LANGUAGE. I checked withgit merge-tree: the merged entrypoint keeps that default and reads the template from the new path. #1384 does not touch these files.Docs: fullstackhero/docs#253 updates the entrypoint snippet in Local development and adds the changelog entry.