Skip to content

feat(comfyui-ui): add new static frontend image#3

Open
arsac wants to merge 12 commits into
mainfrom
feat/comfyui-ui-split-image
Open

feat(comfyui-ui): add new static frontend image#3
arsac wants to merge 12 commits into
mainfrom
feat/comfyui-ui-split-image

Conversation

@arsac
Copy link
Copy Markdown
Owner

@arsac arsac commented Apr 8, 2026

Summary

Adds a new apps/comfyui-ui/ directory producing ghcr.io/arsac/comfyui-ui:<version>. The image is nginx:alpine serving the Comfy-Org/ComfyUI_frontend dist.zip as static files, with reverse-proxying and response caching to the ComfyUI backend pod.

The motivation is splitting comfyui's UI tier from its GPU backend tier so the backend can scale from zero without breaking the browser UI. A follow-up PR in arsac/home-ops will wire up the Deployment, Service, HTTPRoute, and ScaledObject that consume this image.

Design notes

  • Version coupling: VERSION tracks Comfy-Org/ComfyUI_frontend (the static files shipped in this image). COMFYUI_VERSION tracks comfyanonymous/ComfyUI (the backend this UI is paired with). Both are Renovate-managed and grouped via a new packageRule in .renovaterc.json5 so a maintainer sees bumps together. automerge: false on the group so a human verifies that VERSION matches the comfyui-frontend-package version pinned by the new ComfyUI tag's requirements.txt.
  • Variable naming: the bake file uses VERSION (not FRONTEND_VERSION) because the shared CI .github/actions/app-options/action.yaml extracts the version via jq '.[] | select(.name == "VERSION") | .value' and would otherwise get an empty string. The Dockerfile's internal ARG is still FRONTEND_VERSION — the bake file passes VERSION's value into it.
  • Why static files instead of deploying the existing comfyui image twice: the existing RWO PVCs (comfyui, comfyui-cache, comfyui-models) can't be shared across nodes, and ComfyUI has in-memory state (queue, history, loaded models) that can't be shared across processes regardless. A dedicated nginx image is ~50MB vs ~1-2GB for a second ComfyUI Python process, and avoids the state-split entirely.
  • Caching strategy: /object_info, /embeddings, /extensions, /system_stats are cached for 6h with proxy_cache_use_stale on any backend error (500/502/503/504/429), so the UI loads even when the backend is scaled to 0 (after the cache has been primed once). /ws is a direct WebSocket proxy with upgrade headers. All other endpoints (/prompt, /queue, /history, /view, /upload, /api/*, etc.) are direct-proxy with no cache — they return 502 when backend is down, which is correct behavior for user-initiated actions.
  • Runtime configuration: COMFYUI_BACKEND env var specifies the backend Service URL. Defaults to http://comfyui-predictor.ai.svc.cluster.local:80. The entrypoint strips the scheme, rejects path components with a clear error, and renders the host:port into the nginx upstream block via envsubst before nginx starts. nginx -t sanity-checks the rendered config.

Test plan

  • bats apps/comfyui-ui/test/test-build.bats passes locally — 6 tests cover static serving, proxy, cache, stale-on-backend-down, and 502 for non-cached endpoints when backend is down
  • docker buildx bake image-local succeeds
  • docker buildx bake image-all --print resolves cleanly for linux/amd64 and linux/arm64
  • Path-rejection guard verified with docker run -e COMFYUI_BACKEND=http://test:8000/api/v1 — container exits with clear error
  • .github/actions/app-options dry-run returns v1.38.14 for VERSION (was empty before the rename)
  • CI build passes
  • After merge: verify ghcr.io/arsac/comfyui-ui:rolling is tagged and pullable
  • Follow-up in home-ops repo deploys the image and validates end-to-end against the real comfyui-predictor backend

Out of scope

  • Kubernetes manifests to deploy this image — that's a separate PR in arsac/home-ops
  • Migrating existing comfyui consumers to the new split architecture

arsac and others added 12 commits April 8, 2026 13:32
- Change EXPOSE 8188 to EXPOSE 80 (scaffold uses nginx default port;
  Task 3 will set the port when nginx.conf is added)
- Remove LABEL directives — repo convention puts labels in
  docker-bake.hcl and CI's docker-metadata-action sets title
- Remove # syntax=docker/dockerfile:1.9 to match repo style
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cd into app dir before docker buildx bake so default context
  resolves (was failing on any checkout without pre-cached image)
- Add readiness check for fake backend before starting UI to
  eliminate proxy-test race condition

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… http 1.1)

- Drop $request_uri from metadata location proxy_pass so keepalive
  pool is actually used for the cached endpoints
- Reject COMFYUI_BACKEND with a path component explicitly; nginx
  gives a cryptic error otherwise
- Fix misleading variable name in nginx.conf header comment
- Add proxy_http_version 1.1 and Connection "" to dynamic API
  location so keepalive works for the catch-all proxy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Renovate auto-derives groupSlug from groupName when the name is
already a valid slug; setting both was dead config.
…s compatibility

The shared .github/actions/app-options/action.yaml extracts the image version
via jq select(.name == "VERSION"), so the bake variable must be named VERSION.
The Dockerfile ARG remains FRONTEND_VERSION; the bake file passes VERSION's
value into it. README updated to reflect the rename.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant