Aircon
No more worktrees. Aircon gives every feature branch its own isolated Docker container pre-loaded with Claude Code, your credentials, and a running shell — so you can work on multiple branches in parallel without them stepping on each other. Dependencies like databases are isolated, so a db migration in one container does not affect the other.
Each container gets:
- Your Claude Code credentials and settings injected at startup (no Dockerfile changes needed)
- Claude Code installed automatically if not already in the image
- Your GitHub token set for authenticated
gitandghoperations - A git branch checked out and ready to go
- An optional project-specific init script that runs after the container is up
When you close the last shell session, the container and its volumes are automatically torn down.
Prerequisites
- Ruby >= 3.3.0
- Docker and Docker Compose
- Claude Code installed on your host machine (aircon copies credentials from it)
- VS Code with the Dev Containers extension (only needed for
aircon vscode)
Installation
gem install airconOr add to your Gemfile:
gem "aircon"Getting Started
1. Initialize aircon in your project:
cd your-project
aircon initThis creates four files under .aircon/:
| File | Purpose |
|---|---|
aircon.yml |
Main config (tokens, paths, user settings) |
aircon_init.sh |
Script run inside the container after setup |
Dockerfile |
Base image for your dev container, change it or use your own |
docker-compose.yml |
Compose config wired to the Dockerfile, change it or use your own |
Existing files are never overwritten, so aircon init is safe to re-run.
2. Configure your tokens:
Edit .aircon/aircon.yml and set at minimum:
gh_token: <%= ENV['GITHUB_TOKEN'] %>3. Start a container:
aircon up my-featureThis builds the image, injects your Claude credentials, checks out a branch named my-feature, runs your init script, and drops you into a shell inside the container. The container and dependencies set in the docker-compose file will all be running under the my-feature docker project, fully isolating it from other containers.
Commands
aircon up NAME [PORT]
Start or attach to a dev container.
aircon up my-feature # start on default port 3001
aircon up my-feature 3005 # start on port 3005
aircon up my-feature -b feat/auth # use a different git branch than NAME
aircon up my-feature -d # start detached (no interactive shell)What aircon up does, step by step:
- Looks for an existing container named
NAME-SERVICE-1(e.g.my-feature-app-1).- If found → attaches a new
bashsession to it and skips to step 12.
- If found → attaches a new
- Warns if
gh_tokenis not configured. - Runs
docker compose up -d --buildwithHOST_PORT,AIRCON_APP_NAME,AIRCON_CONTAINER_USER, andAIRCON_WORKSPACE_PATHinjected as environment variables. - Copies
~/.claude.jsonand~/.claude/from your host into the container viadocker cp(noCOPYlines needed in your Dockerfile). Host home paths inside those files are rewritten to match the container home directory. - Installs Claude Code inside the container if not already present (
curl -fsSL https://claude.ai/install.sh | bash). - Adds
~/.local/bintoPATHin/etc/bash.bashrcsoclaudeis available in all sessions. - Writes
GH_TOKENandGITHUB_PERSONAL_ACCESS_TOKENto/etc/bash.bashrc(ifgh_tokenis set). - Injects Claude Code credentials based on
credentials_source: copies from macOS Keychain (keychain), from~/.claude/.credentials.json(file), or setsCLAUDE_CODE_OAUTH_TOKENin/etc/bash.bashrc(oauth_token). - Sets
git config user.emailandgit config user.nameglobally inside the container. - Configures
gitto authenticate GitHub URLs with your token (covers bothhttps://github.com/andgit@github.com:). - Checks out the branch:
- If the branch exists on
origin→ fetches and checks it out. - Otherwise → creates a new branch from
origin/main.
- If the branch exists on
- Runs the
init_script(.aircon/aircon_init.shby default) inside the container viabash -l, if the file exists. - Attaches an interactive
bashsession. - When the last
bashsession exits → runsdocker compose down -v --remove-orphansanddocker image prune -f.
Options:
| Option | Alias | Description |
|---|---|---|
--branch BRANCH |
-b |
Git branch to check out (defaults to NAME) |
--detach |
-d |
Start without attaching an interactive session |
aircon down NAME
Tear down the container and volumes for a project.
aircon down my-featureRuns docker compose down -v --remove-orphans and prunes unused images. Use this to clean up manually if you need to reset state without waiting for a session to end.
aircon vscode NAME
Attach VS Code to a running container.
aircon vscode my-featureThe container must already be running (aircon up first). Opens VS Code connected to the container via the Dev Containers extension, with the workspace set to workspace_path.
aircon init
Generate the .aircon/ config files in the current directory.
aircon initCreates aircon.yml, aircon_init.sh, Dockerfile, and docker-compose.yml under .aircon/. Safe to re-run — existing files are not overwritten.
aircon version
Print the installed version.
aircon versionConfiguration
Config is loaded from .aircon/aircon.yml in your project root. All keys are optional. ERB is supported, so you can pull in environment variables.
# Docker Compose file to use
compose_file: .aircon/docker-compose.yml
# Application name — used for DB credentials in the default Compose template
# Defaults to the basename of the current directory
app_name: my-app
# GitHub personal access token — authenticates git and gh inside the container
gh_token: <%= ENV['GITHUB_TOKEN'] %>
# How to obtain Claude Code credentials: "keychain" (macOS), "file", or "oauth_token"
credentials_source: keychain
# Claude Code OAuth token — used when credentials_source is "oauth_token"
# Falls back to CLAUDE_CODE_OAUTH_TOKEN env var if not set here.
claude_code_oauth_token: <%= ENV['CLAUDE_CODE_OAUTH_TOKEN'] %>
# Workspace folder path inside the container
workspace_path: /my-app
# Path to your Claude config file on the host
claude_config_path: ~/.claude.json
# Path to your Claude directory on the host
claude_dir_path: ~/.claude
# Docker Compose service name
service: app
# Git identity inside the container
git_email: claude_docker@localhost.com
git_name: Claude Docker
# Non-root user inside the container (determines home directory)
container_user: appuser
# Script to run inside the container after setup (path relative to this file)
init_script: .aircon/aircon_init.shConfiguration Reference
| Key | Default | Description |
|---|---|---|
compose_file |
.aircon/docker-compose.yml |
Docker Compose file to use |
app_name |
basename of cwd | App name passed to Compose as AIRCON_APP_NAME
|
gh_token |
nil |
GitHub token; sets GH_TOKEN and GITHUB_PERSONAL_ACCESS_TOKEN in the container |
credentials_source |
keychain |
keychain (macOS), file (~/.claude/.credentials.json), or oauth_token
|
claude_code_oauth_token |
nil |
OAuth token for oauth_token mode; falls back to CLAUDE_CODE_OAUTH_TOKEN env var |
workspace_path |
/workspace |
Workspace folder path inside the container |
claude_config_path |
~/.claude.json |
Host path to claude.json
|
claude_dir_path |
~/.claude |
Host path to .claude/ directory |
service |
app |
Docker Compose service name for the main container |
git_email |
claude_docker@localhost.com |
Git author email inside the container |
git_name |
Claude Docker |
Git author name inside the container |
container_user |
appuser |
Non-root user inside the container |
init_script |
.aircon/aircon_init.sh |
Script run after setup; has access to GH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, etc. |
Notes
- SSH keys are not managed by aircon — handle them in your
init_scriptif needed. - Your
Dockerfiledoes not needCOPYinstructions for Claude settings; aircon injects them at runtime viadocker cp. - The Docker Compose project name is set to
NAME(the argument toaircon up), so the container will be namedNAME-SERVICE-1(e.g.my-feature-app-1). This is how aircon identifies containers across commands.
Releasing to RubyGems
- Bump the version in
lib/aircon/version.rb. - run
bundle exec rake release.
You'll be prompted for your RubyGems credentials on first push. Subsequent pushes use the stored API key at ~/.gem/credentials.
License
MIT — see LICENSE.txt.