Build
All images are built with Docker (multi-arch) via GitHub Actions and pushed to GHCR (GitHub Container Registry) under the finaisse-org org.
Builds are normally triggered by a version tag, not by a merge. Pushing <service>-vX.Y.Z builds that service and is the first step of a release — see Deploy for the full path. Manual dispatch remains available for forcing a rebuild.
GitHub
Backend (blitz)
Each microservice in src/apps/ is independently containerised.
Trigger a build via GitHub Actions:
| Workflow | Trigger | Output |
|---|---|---|
build-push-<service>.yml | <service>-vX.Y.Z tag push, or manual | Single service image |
build-push-all.yml | Manual only | All service images |
_build-push-service.yml | workflow_call (reusable) | The shared build used by all 12 |
build-push-all.yml has no schedule. The nightly build was removed (a12f2060) once releases became tag-driven — an unattended :latest rebuild no longer matches how anything is deployed. Do not rely on an overnight build existing.
Image tags:
ghcr.io/finaisse-org/blitz-<service>:latest— most recent build frommainghcr.io/finaisse-org/blitz-<service>:<short-sha>— specific commit (all branches)ghcr.io/finaisse-org/blitz-<service>:<branch-slug>— non-main branches
Services with CI build workflows (12, all of them): api, ws, apimgmt, agents, emailprocessor, wfw, wfwpdf, recon, remotecontrol, excelrw, bapiproxy, classicml.
All 12 route through the one reusable _build-push-service.yml, including the two .NET services (excelrw, bapiproxy) and the Python one (classicml) — it bakes each service's compose target rather than assuming Bun, so "has a build workflow" does not imply "is a Bun service".
The version gate
Every tag-triggered build verifies version monotonicity before pushing: a tag whose version is not strictly higher than every existing tag for that service fails the build, so no bad-version image reaches GHCR. This runs as a step inside the build job (it used to be a separate workflow — one job per service, each billed a full minute for ~19s of work).
It only applies to <service>-vX.Y.Z tag pushes; branch builds and manual dispatches skip it. It never blocks a rollback, because redeploying an already-tagged version via the manifest creates no tag.
Frontend (blitz-ui)
The frontend is a Module Federation multi-app monorepo. Each sub-app is built independently; the ui host shell loads the others at runtime via URLs baked in at build time.
Sub-apps: ui (host), closehub, invoice, journal, recon, collections
Important: VITE_*_REMOTE_URL environment variables must be set correctly before the build runs — they are compiled into the JavaScript bundle and cannot be changed at runtime. All 6 apps are built and deployed in a coordinated pipeline.
Trigger a build: a ui-vX.Y.Z tag push, or GitHub Actions → build-push-ui.yml in the blitz-ui repo.
The UI build is the slowest in the fleet — ~9–10 minutes
Each app needs roughly 6 GB of heap, so the apps are built natively and sequentially (not in Docker, not in parallel) to avoid OOM on a standard runner. Low observed RSS does not mean low demand. This sets the pace of every full release: backend services finish in ~2–3 minutes and then wait on this one.
Admin / mgmt UI (blitz-mgmtui)
The mgmt portal UI (apps/mgmt) is a standalone Vue app (not a federation remote), built and pushed as its own image ghcr.io/finaisse-org/blitz-mgmtui.
CI: a mgmtui-vX.Y.Z tag push, or GitHub Actions → build-push-mgmtui.yml, which builds natively first then packages via the OOM-safe build/Dockerfile.mgmt.prebuilt. Both UI workflows carry the same version gate as the backend builds.
A manual dispatch of build-push-mgmtui.yml also accepts an image_type input (private default / public), producing a separate ghcr.io/finaisse-org/blitz-mgmtui-public image for the public-variant build — not used by the standard release path.
Local
Backend, single service:
cd blitz/src
just docker-build-api # multi-arch build
just docker-push-api # push to GHCRFrontend, tenant UI:
cd blitz-ui/src
just docker-build-ui # tenant UI
just docker-build-mgmtui # admin / mgmt UIAdmin / mgmt UI one-shot build: just docker-build-mgmtui (uses the in-container build/Dockerfile.mgmt, mirroring the tenant UI) then just docker-push-mgmtui. Both are in the blitz-ui justfile. Set VITE_SYNCFUSION_LICENSEKEY in your environment first — the recipe passes it as a build arg.
Build Artifacts
All images are stored in GHCR:
- Backend:
ghcr.io/finaisse-org/blitz-<service> - Frontend:
ghcr.io/finaisse-org/blitz-ui(tenant),ghcr.io/finaisse-org/blitz-mgmtui(admin)
Images are pulled by Railway (staging) at deploy time — at the digest the release manifest pins, not at :latest. See Deploy.
Tag scheme per release: ghcr.io/finaisse-org/blitz-<service>:<service>-vX.Y.Z. The :latest and :<short-sha> tags are still pushed on main builds but nothing deploys from them any more.