Container images, automation, templates, and samples
- 📦 local-registry: local registry install and systemd bootstrap.
- 🧱 dev-base: base image and local registry bootstrap.
- 🛠️ monolith-dev: polyglot devcontainer image for mixed workspace development.
- 🐹 golang-dev: Go devcontainer image.
- 🟢 javascript-dev: JavaScript devcontainer image.
- 🐍 python-dev: Python devcontainer image.
- 🔷 typescript-dev: TypeScript devcontainer image layered on the JavaScript dev image.
- 🔷 dotnet-dev: .NET devcontainer image.
- 🍻 devcontainer-samples/homebrew-into-distroless: Containerfile-only Homebrew builder-stage sample that copies
hellointo a distrolesscore-runtimeimage. - 🗄️ devcontainer-samples/mssql-dev: compose-backed SQL Server devcontainer sample.
- 🧰 devcontainer-samples/podman-in-podman: single-container sample with the nested Podman feature and required runtime arguments.
- 🦑 devcontainer-samples/squid-proxy: compose-backed squid proxy devcontainer sample.
- 📨 devcontainer-samples/smtp4dev: compose-backed smtp4dev devcontainer sample.
- To set up the local registry, run
./local-registry/install.sh. It creates or starts the registry container and enables a lingering systemd user service so it comes back automatically. - To build and publish images, run
./local-registry/build-images.sh --version X.Y.Z.
- Use
.devcontainer/feature-ci/devcontainer.jsonwhen you want a workspace that can run the feature test and pre-publish checks before pushing. - Start it with
dev up --overlay none --config .devcontainer/feature-ci/devcontainer.json, or open that config directly in VS Code. - Install
just,act,podman,devcontainer, andjqin that environment before running the local commands. - Run
just hooksonce in each checkout so git uses the repo-managed.githooks/directory. - Run
just prepareif you want to stage a throwaway feature-ci workspace and verify the toolchain. - Run
just bump-feature-version <feature> <patch|minor|major|set X.Y.Z>after you decide the semver bump for a feature source change. - Run
just check-feature-version-bumpsto validate staged feature-source changes before committing. - Run
just cito execute.github/workflows/test.yamllocally throughact. - Run
just feature homebrew-packagesto test one edited feature locally, including its scenarios. This now targetsdevcontainer-features/directly, so there is no source-tree scratch mirror in the feature validation path. homebrew-packagesuses a prewarmed CI base image and a shared bottle cache so the Homebrew bootstrap and bottle fetches happen once, then get reused across scenarios and local reruns.- Run
just podman-in-podman-smoketo exercise the host-sidedevcontainer up/devcontainer execsmoke path for thepodman-in-podmanfeature through the local Docker-to-Podman shim. - Run
just publish-checkto package a throwaway copy ofdevcontainer-features/locally without publishing anything. That copy still generateshomebrew-packages-additionalfromhomebrew-packagesso there is still one source feature to maintain. This is the local equivalent of the release workflow's packaging step. - Run
just allto execute both the local workflow simulation and the publish packaging check. just ciandjust job ...keep.github/workflows/test.yamlas the source of truth. They pointactat that workflow file directly instead of wrapping the workflow logic in another shell runner.just feature ...andjust publish-checkare local-only developer conveniences. They keep the existingdevcontainer features testanddevcontainer features packagebehavior; the release workflow uses the same generated scratch layout for publish.- The local commands stage their temporary workspace in a
mktempdirectory and clean it up automatically. actdefaults toghcr.io/catthehacker/ubuntu:act-latestfor theubuntu-latestrunner image. Override it withACT_RUNNER_IMAGEif you need a different local image.- The local
actflow expects a Podman-backed Docker API socket at${XDG_RUNTIME_DIR}/podman/podman.sock. If that socket is missing,juststartspodman system servicethere and reuses the same path for repeated runs. - The local
devcontainerflow uses a Docker compatibility shim under the throwaway workspace'sbin/dockerpath sodevcontainer features testcan keep talking to Podman without changing the workflow commands. - Inside the
feature-cidevcontainer you are still doing nested container work through the mounted host Podman stack, with the repo's existing--userns=keep-id, FUSE, and TUN settings. That is convenient for local validation, but it keeps the same reduced isolation trade-off as before.
- Focused images and
monolith-devboth consume these scripts so version pins and install behavior stay aligned. - Keep image-specific policy in the image
Containerfile; keep toolchain installation details in the shared scripts. - Keep shared images generic: install tools and shell capability there, but apply personal dotfiles and prompt config at devcontainer runtime instead of baking host-specific files into image builds.
homebrew-into-distroless is a Containerfile-only sample that installs a Homebrew package in a builder stage and copies it into a distroless runtime image. mssql-dev, squid-proxy, and smtp4dev are compose-backed devcontainer samples. podman-in-podman is a single-container sample that applies the feature with the required nested-container run arguments. See devcontainer-samples/homebrew-into-distroless/README.md, devcontainer-samples/mssql-dev/README.md, devcontainer-samples/podman-in-podman/README.md, devcontainer-samples/squid-proxy/README.md, and devcontainer-samples/smtp4dev/README.md.
- The workspace dev container mounts the host Podman socket so image build and container registry tasks can run inside a more isolated tooling environment, with the understanding that this weakens the isolation a devcontainer would normally provide. This is a repo-specific convenience, not a recommended general pattern.
If a devcontainer.json uses localhost:5000/... with updateRemoteUserUID enabled, Dev Containers can rewrite that image name to localhost/localhost:5000/... during the temporary UID-adjustment build. Podman then rejects the rewritten reference.
Use 127.0.0.1:5000/... instead.