How Hako Works
Architecture and design of hako's environment isolation.
Hako manages isolated development environments by combining git worktrees with deterministic port allocation. This page explains the design and how the pieces fit together.
Git Worktrees
Each branch gets its own worktree, a full working copy of the repository at a separate path. By default, worktrees live at ~/.hako/worktrees/{repo}/{branch}. You can override this with the HAKO_DIR environment variable or the worktree_dir setting in hako.yml.
If the branch already exists, hako checks it out. If it does not exist, hako creates it with git worktree add -b.
Two Execution Modes
Hako supports two ways of running services:
Docker mode uses Docker Compose to manage containers. Each branch gets its own compose project name ({repo}-{branch_slug}), preventing container name collisions. This was hako's original operating mode and remains the default.
Bare mode manages processes directly on the host machine. No Docker required. Processes run in the background with their PIDs tracked in .hako/pids/ and output captured to .hako/logs/.
The mode is determined by your hako.yml: a services: section activates bare mode, a compose file activates Docker mode. If both are present, hako errors. If neither is present, it defaults to Docker mode. See the configuration reference for the mode override.
Port Allocation
Every environment receives a deterministic block of 100 ports in the range 40000-59999. The port base is calculated from a SHA256 hash of repo:branch, so the same branch always gets the same ports across restarts. If a collision is detected, hako probes the next available block.
Each port variable in your config gets a sequential offset from the base: PORT=base, API_PORT=base+1, DB_PORT=base+2, and so on.
For the full design rationale, see Port Allocation.
Environment Files
Hako generates a .env.hako file for each worktree containing HAKO_PORT_BASE, port variables with their assigned values, and any custom variables from the env: section of hako.yml.
File Synchronization
When hako new runs and hako.yml has no secrets: section, these files are copied automatically from the repo root to the worktree:
.env*files (excluding.env.localand.env.hako).npmrc,.nvmrc,.node-version,.tool-versions
Adding a secrets: section to hako.yml replaces this behavior with explicit file handling. See How to Manage Secrets for details.
Bare Mode Runtime
In bare mode, hako creates a .hako/ directory in the worktree (auto-added to .gitignore):
worktree/
.hako/
pids/dev.pid # PID of running process
logs/dev.log # stdout + stderr
manifest.json # runtime stateThe manifest tracks repo, branch, port base, mode, services, and creation time. Health is derived from it: healthy (all services running), degraded (some running), stopped (none running).
Architecture Diagram
main repo (port 3000)
+-- worktree/feature-a (port 42100)
| .env.hako # PORT=42100, API_PORT=42101
| .hako/pids/dev.pid # bare mode only
| .hako/logs/dev.log
| .hako/manifest.json
+-- worktree/feature-b (port 51234)
.env.hako # PORT=51234, API_PORT=51235Auto-detection
Priority order and logic for automatic project detection.
Port Allocation
Understanding how Hako manages deterministic port assignment.
ops.origin: Masakiro Corp.