Production stage runsDocumentation Index
Fetch the complete documentation index at: https://tally.wharflab.com/llms.txt
Use this file to discover all available pages before exploring further.
bundle install without enforcing Bundler deployment mode.
| Property | Value |
|---|---|
| Severity | Warning (default) / Error (no observable Gemfile.lock in build context) |
| Category | Correctness |
| Default | Enabled |
| Auto-fix | Yes (FixSafe) |
Description
Bundler 2.x’s deployment mode is the production contract: it requiresGemfile.lock to exist, refuses to mutate
the lockfile at build time, installs gems into BUNDLE_PATH, and skips dev/test gem groups (when
BUNDLE_WITHOUT is set). The Rails generator template enables it via ENV BUNDLE_DEPLOYMENT="1" in the base
stage precisely because the alternative — letting Bundler resolve afresh on every build — defeats the “the
lockfile is the build input” property and pushes Bundler back into 1.x semantics.
This rule fires when a Ruby-shaped production stage runs bundle install and:
- Neither
ENV BUNDLE_DEPLOYMENT=...(truthy:1/true) norbundle config set [--local|--global] deployment 'true'is in scope at the install site. bundle install --deployment(the deprecated 2.x flag) is treated as compliant for this rule — the separatetally/ruby/deprecated-bundler-install-flagsrule catches that pattern.
tally/ruby/missing-bundle-without-development:
the Dockerfile binds RAILS_ENV (or RACK_ENV) to "production" somewhere, or the stage has no explicit
non-production marker and the rest of the stage shape (Ruby base, no dev-stage name) implies a runtime image.
Stages explicitly named dev, development, test, testing, ci, or debug are skipped, as are stages
whose effective env binds RAILS_ENV/RACK_ENV to development or test. Non-Ruby stages and Windows
stages are also skipped.
The compliance check honors POSIX/Docker ordering semantics:
ENV BUNDLE_DEPLOYMENTis evaluated at the env state visible to the install RUN. AnENVthat lands AFTER the install does not retroactively make the install compliant.bundle config set ... deployment ...is recognized either when it runs in an earlier RUN, or when it precedes thebundle installin the same RUN’s parsed command chain. Abundle config setin a LATER RUN is not enough — Bundler config only affects subsequent installs.
Context-aware refinement
When tally is invoked with--context (or via Bake/Compose), the rule consults the project’s Gemfile.lock:
- Severity escalation when no lockfile exists. If
Gemfile.lockis not observable in the build context (no checked-in lockfile, or.dockerignoreexcludes it), the rule’s severity escalates fromwarningtoerror. Without a lockfile, Bundler resolves dependencies fromGemfileafresh on every build, which produces non-deterministic images. Combined with the missingBUNDLE_DEPLOYMENT, this is a hard reproducibility failure, not just a hygiene issue. - Fix wording when the stage has
bundle config set frozen 'true'. The detail text mentions thatBUNDLE_DEPLOYMENT=1is the strict superset —frozenonly locks the lockfile, whiledeploymentalso pinsBUNDLE_PATH, requires the lockfile to exist, and excludes dev/test gems. The fix itself is unchanged.
Examples
Before
After
The Rails-generator-style fix declaresBUNDLE_DEPLOYMENT once at the top of the stage so every nested
bundle install (including any inside CI scripts or entrypoints) honors it:
bundle config set --local deployment 'true' is also accepted:
Auto-fix
The rule offers aFixSafe that inserts ENV BUNDLE_DEPLOYMENT="1" on the line immediately following the
stage’s FROM. Insertion is zero-width at column 0, so it composes cleanly with other rule edits in the same
stage.
References
- Rails Dockerfile generator template —
emits
ENV BUNDLE_DEPLOYMENT="1"in the base stage. - Bundler
bundle installdocumentation — deployment-mode contract. - Bundler 2 upgrade guide — deprecation of
bundle install --deploymentflag form.