Hako
Explanation

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.local and .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 state

The 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=51235

ops.origin: Masakiro Corp.

On this page