BoringBuilder
BoringBuilder turns application source into deployable artifacts and container images with Dagger. Rails is the first-class path, and every application can use the same Mise-powered toolchain from a small Dagger Ruby recipe. Local builds use Dagger's cache; shared builds use BoringCache without changing the recipe.

Getting started
BoringBuilder itself runs on Ruby, but the application being built does not need to. Dagger runs through Apple Container on supported Macs, Docker on Linux or macOS, or an existing Dagger engine on a Linux VM.
Install the builder and run it from an application root:
gem install boringbuilder --version "~> 0.1.0.alpha"
boringbuilder doctor
boringbuilder build
Rails and other Ruby applications can keep the builder in their bundle:
bundle add boringbuilder --version "~> 0.1.0.alpha"
bundle exec boringbuilder build
The default build produces a zstd-compressed artifact with normalized archive metadata. Without shared credentials
it is written under dist/. BoringBuilder automatically:
- loads
config/boringbuilder.rbwhen present; - runs its custom Dagger Ruby recipe for any application; or
- uses the built-in Mise-powered Ruby pipeline when a
Gemfileis present.
The built-in pipeline reads the Ruby toolchain from mise.toml, .mise.toml, .tool-versions, .ruby-version, or
Gemfile, then Gemfile.lock. It installs the selected toolchain with Mise inside the Dagger build. Generated
recipes use that same foundation for Node, Rust, Go, or any other tool available through Mise.
A root Gemfile identifies a Ruby application. Rails receives asset, Bootsnap, entrypoint, and /up health-check
conventions. Hanami, Rack, and Procfile applications receive production commands without pretending to be Rails.
Non-Ruby applications provide the few commands that make their build distinctive; BoringBuilder does not guess a package manager or production output. Generate a detected Node, Rust, Go, Ruby, or generic starting point when needed:
boringbuilder init
boringbuilder build
The generated Ruby stays small: pipeline.mise prepares the tools and source, while pipeline.run persists declared
dependency caches locally or through BoringCache. Builds print clean, numbered application steps while Dagger's
internal graph stays out of the way.
See Building applications for the generated recipes and the complete pipeline API.
Command-line options remain explicit overrides:
boringbuilder build --runtime apple
boringbuilder build --runtime docker --platform linux/arm64
Rails applications can also use the included task:
bin/rails boringbuilder:build
Caching
Every build uses the selected Dagger engine's local cache. When BoringCache credentials and a workspace are present, shared BoringCache entries replace the corresponding local mounts for Mise, Bundler, and declared custom pipeline caches across fresh engines and CI runners.
See Caching for custom cached steps and restore-only builds.
Artifacts and images
The default tar.zst output is intended for atomic filesystem deployers such as BoringDeploy. Without shared
credentials it is written under dist/. With BoringCache save credentials it is published as a shared BoringCache
Artifact instead; pass --output when a local copy is also wanted.
The same build can produce or publish a container image:
boringbuilder build --output dist/app.tar.zst
boringbuilder build --format oci --output dist/app.oci.tar
boringbuilder build --format docker --output dist/app.docker.tar
boringbuilder build --push ghcr.io/acme/app:latest
Application-specific filesystem layouts live in ordinary Ruby:
# config/boringbuilder.rb
BoringBuilder.configure do |config|
config.artifact.directory("/app", at: "/opt/my-app/current")
end
See Artifacts and images for directory selection, OCI and Docker archives, registry publishing, and the Ruby API.
Documentation
- Building applications
- Artifacts and images
- Caching
- Project soul
- Code and writing style
- Agent guide
- Changelog
- Contributing
- Security policy
Development
mise install
bin/setup
bin/ci
The checked-in toolchain is the development default. The gemspec and CI define the supported Ruby range.