Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,30 @@ This replaces the corepack/pnpm/npm-removal block that otherwise lives in each r
`post-create.sh`. Combined with `agents`, a consumer repo's `devcontainer.json` needs no
lifecycle scripts at all.

## `docker`

Gives the container a working Docker daemon so tooling that shells out to Docker — most
notably [testcontainers](https://testcontainers.com) (used by the `zero-cache` Postgres
integration tests) — runs inside the dev container.

- Pulls in the official
[`ghcr.io/devcontainers/features/docker-in-docker`](https://github.com/devcontainers/features/tree/main/src/docker-in-docker)
feature via `dependsOn`, which installs the Docker engine, runs a daemon **inside** the
container, and adds the remote user to the `docker` group (no `sudo` needed).
- Uses Docker-**in**-Docker rather than docker-outside-of-docker on purpose: testcontainers
relies on bind mounts and container-to-container networking, both of which break under the
host-socket approach (path translation) and aren't available in every environment
(Codespaces, CI). A self-contained daemon "just works" everywhere.

```jsonc
"features": {
"ghcr.io/rocicorp/devcontainer-features/docker:1": {}
}
```

This replaces a per-repo `docker-in-docker` feature line and centralizes the pinned version
alongside the other rocicorp features.

## Updating the feature versions everywhere

1. Bump `codexVersion` default (and/or the `dependsOn` claude-code pin) in
Expand Down
11 changes: 11 additions & 0 deletions src/docker/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"id": "docker",
"version": "1.0.0",
"name": "Docker (Docker-in-Docker, testcontainers-ready)",
"description": "Provides a self-contained Docker daemon inside the container via the official Docker-in-Docker feature. Picked over docker-outside-of-docker so testcontainers (bind mounts + container networking) works without depending on a host socket, which keeps it portable across Codespaces, CI, and local Docker/Podman/OrbStack hosts.",
"documentationURL": "https://github.com/rocicorp/devcontainer-features/tree/main/src/docker",
"dependsOn": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"installsAfter": ["ghcr.io/devcontainers/features/node"]
}
10 changes: 10 additions & 0 deletions src/docker/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -euo pipefail

# The Docker engine itself is installed by the official Docker-in-Docker feature pulled in
# via `dependsOn` (it installs first and also adds the remote user to the `docker` group, so
# the daemon is reachable without sudo). This wrapper exists to give every rocicorp repo a
# single, centrally-pinned entry point for Docker — mirroring how `agents` wraps the official
# claude-code / github-cli features — and a place to hang any future testcontainers-specific
# defaults. There is nothing extra to install here.
echo "rocicorp/docker: Docker engine provided by the docker-in-docker dependency; no extra install steps."
12 changes: 12 additions & 0 deletions test/docker/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
set -euo pipefail

# Pulls in the dev container test library (check, reportResults).
source dev-container-features-test-lib

check "docker client on PATH" bash -c "command -v docker"
check "docker daemon reachable" bash -c "docker ps"
# testcontainers shells out to `docker run`; make sure the daemon can actually start a container.
check "can run a container" bash -c "docker run --rm hello-world"

reportResults