Repository navigation
Description
Goal
Cloudhal is "self-hostable" (README), but the only documented self-host path is npm create cloudhal + cloudhal studio on a developer machine. There is no container image, no Dockerfile, no compose file and no self-host guide. Publish an official, versioned image on the GitHub Container Registry so a server owner runs Cloudhal (Studio + API + MCP + the site) with one docker compose up.
Deliverables
1. Image ghcr.io/cloudhal-dev/cloudhal
- Built from the npm release, not from the repository tree:
npm install -g @cloudhal/cli@X.Y.Z(+create-cloudhalfor first-run scaffolding) in a multi-stage build onnode:24-bookworm-slim, non-root user,tini/--init,HEALTHCHECKon/api/instance(or an equivalent public probe). The image is exactly what npm users get, so the npm smoke test already covers it. - Multi-arch:
linux/amd64+linux/arm64(better-sqlite3 prebuilds; verify on both, no in-image compilation). - Tags:
X.Y.Z,X.Y,latest(from release tags),edge(main, nightly),sha-<short>; OCI labels (org.opencontainers.image.*), SBOM + provenance attestations (docker/build-push-actionsbom: true,provenance: mode=max), Trivy scan gate, cosign keyless signing (optional, phase 2). - Variant decision for
render_page(needs Chrome): default image without Chrome;-chromevariant later if there is demand.
2. Runtime changes in @cloudhal/cli / @cloudhal/api (the parts a container cannot do without):
cloudhal studio --host 0.0.0.0/CLOUDHAL_HOST(todayserveNodebinds127.0.0.1).--base-url https://studio.example.com/CLOUDHAL_BASE_URL: Better AuthbaseURLand trusted origins are hard-coded tohttp://localhost:<port>inpackages/cli/src/commands/studio.ts; behind a reverse proxy sign-in and cookies break. TrustX-Forwarded-*only when told (--behind-proxy).- Data on one volume:
/dataholds the project (cloudhal.json,dataDirSQLite + blobs, media). First run with an empty volume scaffolds a project (cloudhal initequivalent, starter chosen by env) instead of failing. - Serving the public site:
cloudhal devis Astro's dev server (drafts visible) andcloudhal buildis static output. Decide and document the self-host site path: (a)cloudhal buildon publish → static files served by the same container (or Caddy/nginx sidecar), or (b) a Node adapter forstarters/blog(@astrojs/node) run as a second process. (a) is the smaller, honest first step; (b) is an RFC. The site must redeploy on publish (hook on the scheduler/publish path). - Mail:
cfg.mailprints invites to stdout; document SMTP as a follow-up. CLOUDHAL_AUTOMATIONS,CLOUDHAL_TRUSTED_ORIGINS,CLOUDHAL_AI_TOKEN,CLOUDFLARE_ACCOUNT_IDdocumented as container env.
3. Compose + docs
deploy/docker/compose.yml(studio + optional Caddy with automatic TLS),.env.example,deploy/docker/README.mdanddocs/guides/self-host.md(requirements, upgrade = pull new tag, backup = the volume orcloudhal export, restore, reverse proxy, health).- Upgrade path:
cloudhalmigrations run on start (they do today onopenLocalStore); state the rule "latestfollows patch releases; pinX.Yin production".
4. CI/CD .github/workflows/docker.yml
- Triggered by
release.ymlafter a successful npm publish (workflow_callwith the version, liketemplate-sync.yml), byworkflow_dispatchwith a version (re-publish / backfill), and nightly frommainforedge(built from the repo, taggededgeonly, neverlatest). permissions: packages: write, id-token: write, attestations: write;docker/login-actionto ghcr withGITHUB_TOKEN;docker/metadata-actionfor tags/labels;docker/setup-qemu-action+buildx; cachetype=gha.- Smoke after build: run the image,
curlthe health endpoint, sign up the first owner through the API, create a site, publish, fetch the site (the same loopnpm-smoke.ymlruns, inside the container). - Dependabot: add the
dockerecosystem for the base image andgithub-actionsalready covers the actions. - Package visibility public on ghcr; link the package to the repository (
org.opencontainers.image.source).
5. Versioning rule
One version everywhere: the changesets fixed group → vX.Y.Z tag → npm → ghcr X.Y.Z. The image never carries its own version; a rebuild of the same version (base-image security fix) is X.Y.Z-r2 / re-tag, documented in CONTRIBUTING.md "Releasing" step 5.
Order
- Runtime changes (
--host,--base-url,/datafirst run, site serving decision) with tests on the Node server. Dockerfile+ compose + local build on amd64/arm64; manual smoke.docker.yml+ release hook + Dependabotdocker; first publish of the current release byworkflow_dispatch.docs/guides/self-host.md, README "Self-host" section, CONTRIBUTING "Releasing" step.
Follow-ups (separate issues): SMTP mail, Chrome variant for render_page, Node adapter for live site serving (RFC), Helm chart / Kubernetes manifests if asked for.
Activity
docs(changeset): release note for the Docker image (#143)
Madraka commentedon Nov 10, 2026
Done. The official image
ghcr.io/cloudhal-dev/cloudhalships with every release since 0.6.3. It is multi-arch (amd64/arm64), carries an SBOM, provenance and attestations, and passes a smoke test and a Trivy scan before the version tags move (#148, #170). The current release is 0.7.0 (tags0.7.0,0.7,latest). The guide is docs/guides/self-host.md. The remaining follow-up, rebuilding the static site on publish, is tracked in #150.