Skip to content

feat(docker): stage per-service runtime trees and lower container image size budgets - #2

Open
dayton536 wants to merge 1 commit into
mainfrom
cursor/per-service-container-images-87b3
Open

dayton536 wants to merge 1 commit into
mainfrom
cursor/per-service-container-images-87b3

Conversation

@dayton536

Copy link
Copy Markdown
Owner

Fixes oll4com#64

Problem and user impact

runtime-base copied the whole monorepo runtime (node_modules, apps, packages, agent skills, and owner scripts) into every image. core therefore shipped node-pty, and browser and cli shipped the API, the web app, the Temporal native bridge, and other packages those processes never start. Image size budgets sat at 1150 / 1750 / 2800 MiB.

Change

The build stage now runs deploy/docker/stage-container-runtime.mjs and writes /app-targets/{core,browser,cli}. Each target copies only that tree.

  • core gets the API, worker, and web server, the runtime @space/* workspace packages they import, agent skills, owner and backup scripts, database migrations, and the production dependency closure. node-pty is excluded.
  • browser gets browser-host-main.js, the local modules it imports, the @space/* packages on that graph, and their production dependencies, including database migrations.
  • cli gets @space/cli-host, agent skills, and node-pty with the Linux native build for the image architecture. Darwin, Windows, and musl prebuilds and the Windows conpty sources are dropped.
  • node-pty loads lazily inside @space/cli-host (createRequire) and is no longer a dependency of @space/api, so core can import the CLI host client without the native module.
  • runtime-base still has the OS packages, the patched global npm, the spaceapp user 10001:10001, licenses, deploy/docker entrypoints, and the *-vscode-parity wrappers. Entrypoints and the core healthcheck are unchanged.
  • Size budgets in scripts/container-image-size-budget.mjs are 850 MiB (core), 1350 MiB (browser), and 1000 MiB (cli) for both linux/amd64 and linux/arm64. The existing budget script API is unchanged, so release.yml keeps the same --target / --image contract.
  • .github/workflows/containers.yml smoke-checks each loaded image with deploy/docker/smoke-container-image.mjs: core starts against the pinned pgvector Postgres, lets the entrypoint migrate, and requires /readyz ok: true; cli accepts a connection on /run/spaceapp-cli/host.sock; browser migrates, accepts a connection on its host socket, and launches headless Chromium once.
  • PUBLIC_EXPORT_MANIFEST.json records the new hashes for the three previously exported files this change edits (apps/api/package.json, package-lock.json, packages/cli-host/src/node-pty-spawn.ts). Those two source files no longer match the private export blobs, so the manifest's transformation count is 423. scripts/public-export.mjs is unchanged.

Verification

  • node --test scripts/tests/public-suite.test.mjs — 154 tests, 0 failures.
  • npm run test:architecture — 7/7; domain boundaries passed (1306 source modules, 3 existing cyclic components).
  • npm test -w @space/cli-host — 3/3.
  • npm run check -w @space/cli-host and npm run check -w @space/api (the API check after the workspace packages it imports were built).
  • node scripts/verify-export-provenance.mjs . — ok: true, no problems.
  • node scripts/public-export.mjs --output /tmp/spaceapp-public-export — audit passed (5103 files, 0 transformations on this public tree).
  • Linux coverage is the container matrix. This environment has no Docker, so image builds, measured sizes, and the launcher install --profile large check were not run here. containers.yml builds both architectures, enforces the budgets, and runs the smoke checks.
  • No UI change.
  • Signed-off-by: Dayton Allen <daytonballen@gmail.com> (Developer Certificate of Origin 1.1). Licensed under the MIT License.
  • No credentials, tokens, personal memory, customer data, private paths, or runtime state.
  • Changelog updated under Unreleased. README disk minima are unchanged: they also cover PostgreSQL, Temporal, checkpoints, and working space, and full images were not measured here.
  • Security, migration, and rollback: entrypoints, the non-root user, the healthcheck, and the npm security patches are unchanged. Compose service names and socket paths are unchanged. Rollback is reverting this commit.

Fixes oll4com#64

Open in Web Open in Cursor 

…dgets

Split the image build so core, browser, and cli each ship only the
workspaces and production dependencies that service runs. node-pty
loads lazily from the CLI host, so the core image can import the CLI
host client without the native module.

Fixes oll4com#64

Signed-off-by: Dayton Allen <daytonballen@gmail.com>
Co-authored-by: dayton536 <dayton536@users.noreply.github.com>
"fetch('http://127.0.0.1:4911/readyz').then(async r=>{const b=await r.json();if(!r.ok||b.ok!==true)process.exit(1)}).catch(()=>process.exit(1))";

function socketProbe(socketPath) {
return `import('node:net').then(({createConnection})=>{const s=createConnection(${JSON.stringify(socketPath)},()=>{s.end();process.exit(0)});s.on('error',()=>process.exit(1))})`;
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.

[$400] Per-service container images: ship each image only the code and dependencies it runs

2 participants