ruby/rubygems · error · ProductionError
can't be updated because file system is read-only
Error message
can't be updated because file system is read-only
What it means
While writing the resolved lockfile, Definition#write_lock opens the file inside SharedHelpers.filesystem_access; an Errno::EROFS from the operating system is normalized to ReadOnlyFileSystemError and re-raised as ProductionError with a lockfile-changes summary ending in file system is read-only (lib/bundler/definition.rb:447). It means resolution changed the lockfile but the storage cannot accept writes: a read-only container filesystem, an immutable production image, or (via the same filesystem_access wrapper) missing write permission on the directory. An up-to-date lockfile triggers no write, so this fires only when a change was actually needed.
Source
Thrown at lib/bundler/definition.rb:447
"Your lockfile needs to be updated, but it can't be because frozen mode is set.\n\n" \
"Run `bundle install` elsewhere and add the updated #{SharedHelpers.relative_lockfile_path} to version control."
raise ProductionError, msg
end
# Convert to \r\n if the existing lock has them, i.e., Windows with
# `git config core.autocrlf=true`. Detect from the bytes on disk because
# reading in text mode strips carriage returns on Windows, which would
# otherwise defeat this check and rewrite a `\r\n` lockfile with `\n`.
if File.exist?(file) && SharedHelpers.filesystem_access(file, :read) {|p| File.binread(p).include?("\r\n") }
contents.gsub!(/\n/, "\r\n")
end
begin
SharedHelpers.filesystem_access(file) do |p|
File.open(p, "wb") {|f| f.puts(contents) }
end
rescue ReadOnlyFileSystemError
raise ProductionError, lockfile_changes_summary("file system is read-only")
end
end
def locked_ruby_version
return unless ruby_version
if @unlocking_ruby || !@locked_ruby_version
Bundler::RubyVersion.system
else
@locked_ruby_version
end
end
def locked_ruby_version_object
return unless @locked_ruby_version
@locked_ruby_version_object ||= begin
unless version = RubyVersion.from_string(@locked_ruby_version)
raise LockfileError, "The Ruby version #{@locked_ruby_version} from " \
"#{@lockfile} could not be parsed. " \View on GitHub (pinned to 86cbb817a3)
Solutions
- Move locking to build time: run `bundle install` during the image build and ship the current Gemfile.lock so runtime needs no write
- If the runtime must lock, give it a writable layer: mount a writable volume on the app dir or drop --read-only
- Fix directory permissions when the filesystem is writable but the user lacks write access
- Check writability before bundler runs: `ruby -e "puts File.writable?(Dir.pwd)"`
Example fix
# before $ docker run --read-only myapp bundle install # => can't be updated because file system is read-only # after (lock at build time) # Dockerfile: COPY Gemfile Gemfile.lock ./ then RUN bundle install $ docker run --read-only myapp bundle exec puma
Defensive patterns
Strategy: validation
Validate before calling
lockfile = Bundler.default_lockfile abort "directory not writable" unless File.writable?(File.dirname(lockfile)) abort "lockfile unwritable" if lockfile.exist? && !File.writable?(lockfile)
Try / catch
begin
Bundler.definition.lock(Bundler.default_lockfile)
rescue Bundler::ProductionError => e
warn e.message if e.message.include?("read-only")
# ship a pre-generated lockfile instead of locking here
end Prevention
- Treat Gemfile.lock as a build artifact: resolve at build time, never at runtime
- Test images with docker run --read-only in CI
- Keep runtime filesystems read-only AND lockfiles current; the error means one of the two slipped
When it happens
Trigger: `bundle install` in a container started with docker --read-only or a Kubernetes readOnlyRootFilesystem pod while Gemfile.lock is stale; runtime re-locking in distroless images; bundle commands on NFS or overlay mounts mounted read-only; an unwritable app directory even on a writable filesystem.
Common situations: Security-hardened deployments run app volumes read-only; an image build changed the Gemfile but shipped a stale lockfile so the entrypoint re-resolves at boot; sidecars running bundle at startup inside read-only containers.
Related errors
- This Bundle hasn't been installed yet. Run `bundle install`
- Your lockfile needs to be updated, but it can't be because f
- The Ruby version #{@locked_ruby_version} from #{@lockfile} c
- Frozen mode is set, but there's no lockfile
- can't be updated because frozen mode is set If this is a de
AI-assisted analysis of ruby/rubygems@86cbb817a3 (2026-08-23).
Data as JSON: /api/errors/c78c9e4de4e29f4e.
Report an issue: GitHub.