Isolated Rails databases for Git worktrees

Posted . Visible to the public.

AI coding agents like Claude Code, Codex, and Cursor can work on multiple tasks in parallel by creating separate Git worktrees. Each worktree has its own working directory, but Rails will normally use the same development and test databases across all worktrees.

This can cause conflicts when agents run tests, change the database schema, or modify test data simultaneously.

Giving worktrees their own database

We can solve this with a small addition to database.yml that automatically detects Git worktrees and adds a worktree-specific suffix to the database name. Developers who don't use worktrees are unaffected. Conventional Git checkouts keep their existing database names.

Add the following ERB block at the beginning of your existing config/database.yml:

<%
  # Give Git worktrees separate databases so parallel AI agents don't conflict.
  require 'digest'
  git = File.join((Rails.root rescue Dir.pwd).to_s, '.git')
  id = File.read(git)[%r{/worktrees/([^/\n]+)\s*\z}, 1] if File.file?(git)
  worktree_suffix = "_#{id.underscore.gsub(/[^a-z0-9_]/, '_')[0, 16]}_#{Digest::SHA256.hexdigest(git)[0, 8]}" if id
%>

Then insert the suffix into your existing development and test database names:

development:
  database: myapp<%= worktree_suffix %>_development

test:
  database: myapp<%= worktree_suffix %>_test<%= ENV['TEST_ENV_NUMBER'] %>

Keep your existing adapter, credentials, and other database settings. A complete database.yml example is included below.

The detection reads the worktree's .git file, which points to its Git metadata. No Git subprocess is required. The underscore method requires ActiveSupport.

Why truncate and hash? PostgreSQL limits database names to 63 bytes. Long worktree names can otherwise be silently truncated, potentially causing multiple databases or parallel test workers to share the same name.

Our suffix combines the first 16 characters of the readable worktree ID with an 8-character hash of its full path. This keeps names recognizable while making collisions unlikely. Assuming an application prefix of at most 25 ASCII characters, the resulting database names fit within PostgreSQL's limit, including _development.

Example database names

Assuming the application normally uses myapp_development and myapp_test:

Checkout Development database Test database
Normal checkout myapp_development myapp_test
Worktree feature-login myapp_feature_login_a1b2c3d4_development myapp_feature_login_a1b2c3d4_test
Worktree brave-panda myapp_brave_panda_8e7f6a5b_development myapp_brave_panda_8e7f6a5b_test
Worktree 883f myapp_883f_12ab34cd_development myapp_883f_12ab34cd_test
Worktree feature-add-a-really-long-payment-checkout-flow myapp_feature_add_a_re_91ca82bf_development myapp_feature_add_a_re_91ca82bf_test

Hash values are illustrative. The readable portions are based on Git's internal worktree IDs, which may differ from branch names or directory names.

This works regardless of which AI tool created the worktree. It also works with manually created Git worktrees and doesn't require installing additional tools.

Caveats

  • Database names are isolated, but databases aren't automatically created or migrated. Every new worktree needs its own database setup. You can tell your coding agents to do that for you.
  • Databases may remain after a worktree is deleted and can be cleaned up manually.
  • Your project might use other storage services (like Redis). These are not isolated with this database.yml snippet.
  • The naming scheme assumes an application database prefix of at most 25 ASCII characters. Short hashes make collisions unlikely, but cannot guarantee uniqueness.
  • Existing worktrees using a previous version of this naming scheme will get new database names and need to initialize their databases again. Conventional checkouts remain unaffected.

Tell your coding agents

Coding agents may be surprised when a new worktree begins without a database. They will recover from this, but you can save reasoning roundtrips by telling your agent beforehand.

Add this rule to your project's AGENTS.md or CLAUDE.md:

Each Git worktree has its own development and test databases. After creating a worktree, run `bin/rails db:prepare` to initialize both.

If you're feeling lucky, you can also tell your agent to delete worktree databases when they remove a worktree.

Appendix: Complete database.yml

For reference, here is a complete PostgreSQL database.yml that combines worktree isolation with some additional defaults we commonly use:

  • Environment variables for database credentials and host
  • Support for DATABASE_URL in CI
  • Database access from Claude Code sandboxes
  • Connection pooling based on RAILS_MAX_THREADS
  • A 30-second PostgreSQL statement timeout
  • Separate test databases for parallel test workers

Replace myapp with your application's database prefix (at most 25 ASCII characters).

<%
  # Give Git worktrees separate databases so parallel AI agents don't conflict.
  require 'digest'
  git = File.join((Rails.root rescue Dir.pwd).to_s, '.git')
  id = File.read(git)[%r{/worktrees/([^/\n]+)\s*\z}, 1] if File.file?(git)
  worktree_suffix = "_#{id.underscore.gsub(/[^a-z0-9_]/, '_')[0, 16]}_#{Digest::SHA256.hexdigest(git)[0, 8]}" if id
%>

# Allow CI to control database settings via ENV variable
<% unless ENV['DATABASE_URL'] %>
default: &default
  adapter: postgresql
  encoding: unicode
  username: <%= ENV['DATABASE_USER'] %>
  password: <%= ENV['DATABASE_PASSWORD'] %>
  # https://makandracards.com/makandra/626587-claude-code-sandbox#section-database-access-sandbox
  host: <%= ENV.fetch('DATABASE_HOST', 'localhost') unless ENV['SANDBOX_RUNTIME'] %>
  # See https://guides.rubyonrails.org/configuring.html#database-pooling
  pool: <%= ENV.fetch('RAILS_MAX_THREADS') { 5 } %>
  variables:
    # See https://makandracards.com/makandra/623000-timeouts-long-running-sql-queries
    statement_timeout: 30s

development:
  <<: *default
  database: myapp<%= worktree_suffix %>_development

test:
  <<: *default
  database: myapp<%= worktree_suffix %>_test<%= ENV['TEST_ENV_NUMBER'] %>
<% end %>
Profile picture of Henning Koch
Henning Koch
Last edit
Henning Koch
License
Source code in this card is licensed under the MIT License.
Posted by Henning Koch to makandra dev (2026-10-09 08:00)