Skip to content

ActiveJob-only k8s deployment, thin base image#73764

Open
carl-codeorg wants to merge 13 commits into
stagingfrom
feat/docker-thin
Open

ActiveJob-only k8s deployment, thin base image#73764
carl-codeorg wants to merge 13 commits into
stagingfrom
feat/docker-thin

Conversation

@carl-codeorg

@carl-codeorg carl-codeorg commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds an ActiveJob-only Skaffold deployment path and restructures the k8s Docker image lineage around a slim official Ruby base image.

The image hierarchy now follows the Rails-style split:

  • base: runtime OS packages only, from ruby:3.2.11-slim-bookworm
  • build: compiler/toolchain layer used for bundle, uv, and frontend build work
  • runtime: non-root production image with built artifacts and no dev toolchain
  • dev: same base plus local development tools such as build-essential, Node, Chromium, zsh, and awscli

It also adds an activejob-only Skaffold profile that builds a smaller runtime image with the frontend build skipped and a narrower Bundler group set. This gives us a working path for ActiveJob experiments without carrying the full frontend asset build through each iteration. This PR doesn't get a cold Skaffold build to complete within the 20-minute goal, but I figured we should start committing some of these base changes first before continuing to iterate.

The changes to the base image were generated using Codex 5.5 following an OpenSpec design from Fable 5.

Details

  • Replaces the Ubuntu/rbenv Ruby core image with pinned ruby:3.2.11-slim-bookworm.
  • Removes Ruby compilation from cold Docker builds.
  • Adds jemalloc through libjemalloc2 and an arch-independent LD_PRELOAD path.
  • Uses Bundler deployment mode in runtime images while leaving the dev target mutable.
  • Moves bootsnap out of the development-only Gemfile group and precompiles bootsnap caches during image build.
  • Keeps the existing five-artifact Skaffold graph for cache behavior and context scoping.
  • Updates mimic fixtures and profile wiring so skaffold build -p mimic --cache-artifacts=false still validates Dockerfile changes.
  • Fixes runtime issues found during validation, including missing uv, missing JS runtime behavior for ActiveJob, and zsh assumptions in container commands.

Changes to the main Dashboard app

Most of this PR is scoped to the k8s directory, making it very safe in general. However, there are two changes outside of k8s:

  • Bootsnap moved out of dev dependencies in the Gemfile. If there is a reason we need to keep this as a dev dependency, let me know. Otherwise, it seems the modern Rails way is to use Bootsnap to speed up the boot process in production. The Rails-generated Dockerfiles do this. Summary of what we get from using bootsnap outside of dev:

    • runtime image can install and use Bootsnap
    • build stage can precompile Bootsnap caches
    • development still gets Bootsnap
    • BUNDLE_WITHOUT=development:test no longer removes a gem the image build/runtime wants
  • development.rb and the Prosopite initializer changed to check for Gem.loaded_specs.key?('prosopite'). This avoids situations where the development Rails config is loaded, but the prosopite gem is missing because of BUNDLE_WITHOUT=development:test being set during Docker build.

Validation

  • skaffold dev --trigger=manual -p activejob-only -p setup-db-minimal
  • skaffold build --cache-artifacts=false
  • skaffold build -p mimic --cache-artifacts=false
  • Runtime boot smoke against seeded local DB
  • Dev target bundle install with native-extension coverage and one dashboard spring testunit
  • dive size comparison recorded
  • openspec validate docker-rails-way-image --strict
  • git diff --check
  • ./tools/hooks/pre-commit

Notes

The generic cold skaffold build --cache-artifacts=false completed, but build time is still not acceptable long-term. Measured wall clock was 8223.02s; most of the time was spent in Bundler, especially around devise_invitable. This PR accepts that as follow-up work because the new image structure is functional and compatible with the existing Skaffold workflow.

Ruby 3.2 is still past upstream EOL. This PR pins the slim image digest and makes a later Ruby 3.4 upgrade much smaller, but it does not perform that upgrade.

Image size improvements from switching to a ruby-slim base instead of Ubuntu:

Image Size with ubuntu base Size with ruby-slim base
full code-dot-org 8GB 6.6GB
code-dot-org-core 2.67GB 613MB
active-job-only 4.1GB 1.87GB

Links

  • Jira:

Testing story

Deployment notes

Privacy and security

nicklathe and others added 13 commits June 29, 2026 10:52
Signed-off-by: Nick Lathe <nick.lathe@code.org>
Planning artifacts only, no implementation. Six OpenSpec changes covering
the docker image rework (rails-way multi-stage targets, thin runtime
composition), the k8s process model, secrets rotation, a devcontainer,
and CI seeded-DB snapshots. Each change has proposal/design/specs/tasks
and validates with `openspec validate`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@carl-codeorg carl-codeorg marked this pull request as ready for review July 9, 2026 23:04
@carl-codeorg carl-codeorg requested a review from a team July 9, 2026 23:04
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.

2 participants