Run Dev Containers from the terminal.
Install • Quick Start • Commands • Configuration • Security Model • Development
hatchctl is a Go CLI for creating, inspecting, and using
Dev Container based workspaces without depending on editor integration. It is
built for terminal-first workflows that start a workspace, open a shell,
show resolved configuration, and run life cycle hooks again from the command line.
Note
hatchctl supports macOS and Linux. The optional bridge for browser opening and
localhost callback forwarding is macOS-only.
- Terminal-first Dev Containers: use Dev Containers without opening VS Code
- Repeatable workflows: build, start, inspect, and exec through a single CLI
- Script-friendly output: use JSON output for automation and CI-style tooling
- Backend flexibility: supports Docker by default, with
Podmansupport available - Safer defaults: trust-sensitive repo settings stay gated behind explicit trust flags
Install with mise:
mise use github:lauritsk/hatchctl@latestInstall from source:
go install github.com/lauritsk/hatchctl/cmd/hatchctl@latestPrebuilt binaries for macOS and Linux are published on the +GitHub Releases page.
hatchctl shells out to a container backend instead of talking to an engine
API directly.
You will need:
- Docker or
Podmanavailable onPATH - A Linux container runtime target for Dev Containers
- Compose support from your selected backend when using compose-based Dev Containers
- macOS to use bridge support
Create or reuse the Dev Container for the current workspace:
hatchctl upOpen a shell inside the managed container:
hatchctl execRun a one-off command:
hatchctl exec -- go test ./...Inspect the resolved config and detected runtime state:
hatchctl configRun life cycle hooks again:
hatchctl run --phase startUse JSON output in scripts:
hatchctl up --json
hatchctl config --json
hatchctl exec --json -- sh -lc 'go test ./...'Tip
Use -- with exec to separate hatchctl flags from the command you want to
run inside the container.
hatchctl up: resolve configuration, then create or reconnect to the managed Dev Container, building it first when neededhatchctl build: build the Dev Container image without starting ithatchctl exec: open a shell or run a command inside the managed containerhatchctl config: show merged config and detected runtime statehatchctl run: run Dev Container life cycle phases again in an existing containerhatchctl bridge doctor: inspect bridge availability and current bridge session statehatchctl version: print version information
Common examples:
hatchctl --backend auto up
hatchctl up --workspace ../my-project
hatchctl up --dotfiles lauritsk/dotfiles
hatchctl up --ssh
hatchctl up --trust-workspace --allow-host-lifecycle
hatchctl build --json
hatchctl exec --env CI=1 -- sh -lc 'make test'
hatchctl run --phase attach
hatchctl bridge doctorhatchctl reads configuration from:
- user config
- Linux:
~/.config/hatchctl/config.toml - macOS:
~/Library/Application Support/hatchctl/config.toml
- Linux:
- workspace config:
.hatchctl/config.toml
Workspace config is intentionally limited until you explicitly trust the repository.
Example config:
backend = "auto"
config = ".devcontainer/devcontainer.json"
feature_timeout = "2m"
lockfile_policy = "auto"
[dotfiles]
repository = "lauritsk/dotfiles"
[verification]
[[verification.trusted_signers]]
issuer = "https://token.actions.githubusercontent.com"
subject_regexp = "^https://github.com/lauritsk/hatchctl/.+@refs/tags/.+$"Useful environment variables:
HATCHCTL_TRUST_WORKSPACE=1HATCHCTL_ALLOW_HOST_LIFECYCLE=1HATCHCTL_COSIGN_STRICT=1HATCHCTL_ALLOW_INSECURE_FEATURES=1HATCHCTL_DOTFILES_REPOSITORYHATCHCTL_DOTFILES_INSTALL_COMMANDHATCHCTL_DOTFILES_TARGET_PATH
hatchctl assumes devcontainer.json and workspace-local config may come from
a repository you do not fully trust yet.
Important
Host-affecting behavior is opt-in. If a workspace wants extra host access,
hatchctl stops and tells you which trust flag to add.
Security defaults include:
- Host-side
initializeCommandis blocked unless you pass--allow-host-lifecycle - Repo-controlled backend settings that expand host access are blocked unless
you pass
--trust-workspace - Unsigned images warn by default; enable
HATCHCTL_COSIGN_STRICT=1to block execution - Unsigned remote
OCIfeatures fail by default in unattended runs - Direct tarball features must use
https, except loopbackhttpfor local development and tests - The macOS bridge uses only loopback addresses
See the security policy for the project security policy and reporting contact.
This repository uses mise for tooling and task
orchestration.
Common commands:
mise run fix
mise run lint
mise run test
mise run test:integration
mise run test:coverage
mise run test:race
mise run build
mise run check
mise run release:verify
mise run run -- upFor contributor workflow and release details, see the contributor guide.
Release checksums are signed without keys by Cosign using GitHub Actions OIDC.
cosign verify-blob hatchctl_checksums.txt \
--bundle hatchctl_checksums.txt.sigstore.json \
--certificate-identity \
"https://github.com/lauritsk/hatchctl/"\
".github/workflows/release.yml@refs/tags/vX.Y.Z" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"