bhandras/container-devshell

Project-scoped Ubuntu devshells for Apple container with Python, Go, Rust, Node.js, GitHub CLI, and developer tooling.

★ 0Forks 0ShellGitHub ↗Compare

README

Apple Container Devshell

This repository builds an Ubuntu development image for Apple's container runtime and provides shell helpers for opening project-scoped dev environments.

The default workflow is a named, long-running container:

devshell-start /path/to/project

That command creates a container named devshell if it does not already exist, bind-mounts only the chosen directory at /workspace, keeps the container running in the background, and opens a shell into it. Later shells attach to the same named container:

devshell-shell

Mental Model

  • container build creates an image. The image is the reusable Ubuntu template.
  • devshell-start <dir> creates or enters a named ordinary container from that image. This is the default workflow.
  • The named container keeps running until it is stopped or deleted.
  • The named container has exactly the host directory passed at creation time mounted at /workspace.
  • A container's mounts are fixed when the container is created. To use a different host directory with the same name, delete and recreate the devshell.

Included Tools

  • Ubuntu 24.04
  • Python 3, venv, pip, and uv
  • Go from the current stable upstream tarball
  • Rust stable through rustup
  • Node.js LTS, npm, Corepack, and Yarn
  • Git, Git LFS, and GitHub CLI
  • make, clang, clangd, lld, cmake, ninja
  • Neovim, ripgrep, fd, fzf, jq, tmux, zsh, tree
  • PostgreSQL client and SQLite CLI
  • Docker CLI, Buildx, and Compose plugin

Setup

Source the helper file from zsh or bash:

cd /path/to/container-devshell
source ./devshell.sh

To load the helpers in every new terminal, add the source line to ~/.zshrc:

source /path/to/container-devshell/devshell.sh

The helper defaults are:

DEVSHELL_NAME=devshell
DEVSHELL_IMAGE=local/devshell-ubuntu:latest
DEVSHELL_CPUS=8
DEVSHELL_MEMORY=16G
DEVSHELL_WORKDIR=/workspace

DEVSHELL_NAME controls the default named container.

Build The Image

Build the Ubuntu image:

devshell-build

Or run the underlying Apple commands from this repository:

container builder start --cpus 6 --memory 12G
container build --cpus 6 --memory 12G -t local/devshell-ubuntu:latest -f Containerfile .

container builder start starts Apple's build service. container build reads Containerfile, installs the tools, and stores the result as local/devshell-ubuntu:latest.

Default Workflow

Create or enter the default devshell:

devshell-start /path/to/project

Inside the shell:

pwd      # /workspace
whoami   # your macOS username
ls

Files under /workspace are files from the host directory passed to devshell-start. Files written there persist on macOS. Files written elsewhere live in the named container until it is deleted.

Open another shell into the same running devshell:

devshell-shell

Stop the devshell:

devshell-stop

Start it again and enter a shell:

devshell-shell

Delete the devshell and its container-local filesystem:

devshell-delete

Deleting the devshell does not delete files in the mounted host directory.

Multiple Named Devshells

Pass a name as the second argument to create a separate long-running devshell for another project:

devshell-start /path/to/project-a project-a
devshell-start /path/to/project-b project-b

Enter one by name:

devshell-shell project-a
devshell-shell project-b

Stop or delete one by name:

devshell-stop project-a
devshell-delete project-a

The mount for a named devshell is set at creation time. To point project-a at a different host directory:

devshell-delete project-a
devshell-start /new/path/to/project-a project-a

Helper Commands

devshell-build

Builds DEVSHELL_IMAGE from this repository's Containerfile.

devshell-build

devshell-create <host-dir> [name]

Creates a named, detached container with <host-dir> mounted at DEVSHELL_WORKDIR, which defaults to /workspace. If [name] is omitted, DEVSHELL_NAME is used.

devshell-create /path/to/project
devshell-create /path/to/project my-project

devshell-start <host-dir> [name]

Creates the named devshell if needed, then opens a shell into it. If the devshell already exists, its existing mount is used.

devshell-start /path/to/project

devshell-shell [name]

Opens a shell into an existing named devshell. If it was stopped, container start is run first.

devshell-shell
devshell-shell my-project

devshell-status [name]

Lists devshell containers in a compact table with source and destination mount columns. With a name, only that container is shown.

devshell-status
devshell-status my-project

devshell-stop [name]

Stops a named devshell. The container and its filesystem remain available.

devshell-stop
devshell-stop my-project

devshell-delete [name]

Deletes a named devshell. This removes the container-local filesystem, but not the mounted host directory.

devshell-delete
devshell-delete my-project

Verify Access

Check the named-container workflow:

devshell-start /path/to/project
pwd
whoami
touch .devshell-write-test
exit
ls -l /path/to/project/.devshell-write-test
rm /path/to/project/.devshell-write-test

Check the mounted directory from macOS:

container list --all
devshell-status

Useful Raw Commands

List containers:

container list --all

Enter a named devshell without the helper:

container start devshell
container exec -i -t --user "$(id -un)" -w /workspace devshell /bin/bash

Stop or delete a named devshell without the helper:

container stop devshell
container delete --force devshell

Contributors

bhandras

Issues