Skip to content

Commit d3971c3

Browse files
Add rootless Docker support and update documentation (#1549)
* Add rootless Docker support and update documentation * Ensure parent directory exists before creating source socket * fixing errors * Resolving conflicts * fix(docker-outside-of-docker): update default for installDockerComposeSwitch and remove obsolete ROOTLESS_DOCKER.md documentation * Bump version to 1.9.0 * Delete test/docker-outside-of-docker/custom_rootless_socket_path.sh similar scenario already exists * Delete test/docker-outside-of-docker/xdg_runtime_dir_socket.sh * streamline Docker CLI and Buildx installation process * fixing syntax errors * fixing syntax errors * Enhance Docker installation script for backward compatibility and update root Docker socket detection --------- Co-authored-by: Kaniska <kaniska244@github.com>
1 parent a2fd449 commit d3971c3

File tree

7 files changed

+161
-53
lines changed

7 files changed

+161
-53
lines changed

src/docker-outside-of-docker/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Re-use the host docker socket, adding the Docker CLI to a container. Feature inv
2323
| dockerDashComposeVersion | Compose version to use for docker-compose (v1 or v2 or none or latest) | string | latest |
2424
| installDockerBuildx | Install Docker Buildx | boolean | true |
2525
| installDockerComposeSwitch | Install Compose Switch (provided docker compose is available) which is a replacement to the Compose V1 docker-compose (python) executable. It translates the command line into Compose V2 docker compose then runs the latter. | boolean | true |
26+
| socketPath | Path where the Docker socket is mounted inside the container. For rootless Docker, override the mount in devcontainer.json to map your host socket to this path. | string | /var/run/docker-host.sock |
2627

2728
## Customizations
2829

@@ -36,6 +37,30 @@ Re-use the host docker socket, adding the Docker CLI to a container. Feature inv
3637
- The host and the container must be running on the same chip architecture. You will not be able to use it with an emulated x86 image with Docker Desktop on an Apple Silicon Mac, for example.
3738
- This approach does not currently enable bind mounting the workspace folder by default, and cannot support folders outside of the workspace folder. Consider whether the [Docker-in-Docker Feature](../docker-in-docker) would better meet your needs given it does not have this limitation.
3839

40+
## Rootless Docker Support
41+
42+
By default, this feature expects the Docker socket at `/var/run/docker.sock` on the host, which works for standard (root) Docker installations. For **rootless Docker** setups where the socket is located at `/run/user/$UID/docker.sock` or `$XDG_RUNTIME_DIR/docker.sock`, you need to override the mount in your `devcontainer.json`:
43+
44+
```json
45+
{
46+
"features": {
47+
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
48+
},
49+
"mounts": [
50+
{
51+
"source": "/run/user/1000/docker.sock",
52+
"target": "/var/run/docker-host.sock",
53+
"type": "bind"
54+
}
55+
]
56+
}
57+
```
58+
59+
**Notes:**
60+
- Replace `1000` with your actual user ID (run `id -u` to find it)
61+
- The feature will automatically detect the socket at `/var/run/docker-host.sock`
62+
- Your custom mount will override the feature's default mount
63+
3964
## Supporting bind mounts from the workspace folder
4065

4166
A common question that comes up is how you can use `bind` mounts from the Docker CLI from within the a dev container using this Feature (e.g. via `-v`). If you cannot use the [Docker-in-Docker Feature](../docker-in-docker), the only way to work around this is to use the **host**'s folder paths instead of the container's paths. There are 2 ways to do this

src/docker-outside-of-docker/devcontainer-feature.json

Lines changed: 52 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,58 @@
11
{
22

3-
"id": "docker-outside-of-docker",
4-
"version": "1.9.0",
5-
"name": "Docker (docker-outside-of-docker)",
6-
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker",
7-
"description": "Re-use the host docker socket, adding the Docker CLI to a container. Feature invokes a script to enable using a forwarded Docker socket within a container to run Docker commands.",
8-
"options": {
9-
"version": {
10-
"type": "string",
11-
"proposals": [
12-
"latest",
13-
"none",
14-
"20.10"
15-
],
16-
"default": "latest",
17-
"description": "Select or enter a Docker/Moby CLI version. (Availability can vary by OS version.)"
18-
},
19-
"moby": {
20-
"type": "boolean",
21-
"default": true,
22-
"description": "Install OSS Moby build instead of Docker CE"
23-
},
24-
"mobyBuildxVersion": {
25-
"type": "string",
26-
"default": "latest",
27-
"description": "Install a specific version of moby-buildx when using Moby"
28-
},
29-
"dockerDashComposeVersion": {
30-
"type": "string",
31-
"enum": [
32-
"none",
33-
"latest",
34-
"v1",
35-
"v2"
36-
],
37-
"default": "latest",
38-
"description": "Compose version to use for docker-compose (v1 or v2 or none or latest)"
39-
},
40-
"installDockerBuildx": {
41-
"type": "boolean",
42-
"default": true,
43-
"description": "Install Docker Buildx"
3+
"id": "docker-outside-of-docker",
4+
"version": "1.9.1",
5+
"name": "Docker (docker-outside-of-docker)",
6+
"documentationURL": "https://github.com/devcontainers/features/tree/main/src/docker-outside-of-docker",
7+
"description": "Re-use the host docker socket, adding the Docker CLI to a container. Feature invokes a script to enable using a forwarded Docker socket within a container to run Docker commands.",
8+
"options": {
9+
"version": {
10+
"type": "string",
11+
"proposals": [
12+
"latest",
13+
"none",
14+
"20.10"
15+
],
16+
"default": "latest",
17+
"description": "Select or enter a Docker/Moby CLI version. (Availability can vary by OS version.)"
18+
},
19+
"moby": {
20+
"type": "boolean",
21+
"default": true,
22+
"description": "Install OSS Moby build instead of Docker CE"
23+
},
24+
"mobyBuildxVersion": {
25+
"type": "string",
26+
"default": "latest",
27+
"description": "Install a specific version of moby-buildx when using Moby"
28+
},
29+
"dockerDashComposeVersion": {
30+
"type": "string",
31+
"enum": [
32+
"none",
33+
"latest",
34+
"v1",
35+
"v2"
36+
],
37+
"default": "latest",
38+
"description": "Compose version to use for docker-compose (v1 or v2 or none or latest)"
39+
},
40+
"installDockerBuildx": {
41+
"type": "boolean",
42+
"default": true,
43+
"description": "Install Docker Buildx"
44+
},
45+
"installDockerComposeSwitch": {
46+
"type": "boolean",
47+
"default": false,
48+
"description": "Install Compose Switch (provided docker compose is available) which is a replacement to the Compose V1 docker-compose (python) executable. It translates the command line into Compose V2 docker compose then runs the latter."
49+
},
50+
"socketPath": {
51+
"type": "string",
52+
"default": "/var/run/docker-host.sock",
53+
"description": "Path where the Docker socket is mounted inside the container. For rootless Docker, override the mount in devcontainer.json to map your host socket to this path."
54+
}
4455
},
45-
"installDockerComposeSwitch": {
46-
"type": "boolean",
47-
"default": true,
48-
"description": "Install Compose Switch (provided docker compose is available) which is a replacement to the Compose V1 docker-compose (python) executable. It translates the command line into Compose V2 docker compose then runs the latter."
49-
}
50-
},
5156
"entrypoint": "/usr/local/share/docker-init.sh",
5257
"customizations": {
5358
"vscode": {

src/docker-outside-of-docker/install.sh

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ MOBY_BUILDX_VERSION="${MOBYBUILDXVERSION:-"latest"}"
1313
DOCKER_DASH_COMPOSE_VERSION="${DOCKERDASHCOMPOSEVERSION:-"latest"}" # v1 or v2 or none or latest
1414

1515
ENABLE_NONROOT_DOCKER="${ENABLE_NONROOT_DOCKER:-"true"}"
16-
SOURCE_SOCKET="${SOURCE_SOCKET:-"/var/run/docker-host.sock"}"
16+
SOCKET_PATH="${SOCKETPATH:-"/var/run/docker-host.sock"}" # From feature option
17+
SOURCE_SOCKET="${SOURCE_SOCKET:-"${SOCKET_PATH}"}"
1718
TARGET_SOCKET="${TARGET_SOCKET:-"/var/run/docker.sock"}"
1819
USERNAME="${USERNAME:-"${_REMOTE_USER:-"automatic"}"}"
1920
INSTALL_DOCKER_BUILDX="${INSTALLDOCKERBUILDX:-"true"}"
@@ -318,23 +319,31 @@ else
318319
buildx=(moby-buildx${buildx_version_suffix})
319320
fi
320321
apt-get -y install --no-install-recommends ${cli_package_name}${cli_version_suffix} "${buildx[@]}" || { err "It seems packages for moby not available in OS ${ID} ${VERSION_CODENAME} (${architecture}). To resolve, either: (1) set feature option '\"moby\": false' , or (2) choose a compatible OS version (eg: 'ubuntu-24.04')." ; exit 1 ; }
321-
apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping."
322+
if [ "${DOCKER_DASH_COMPOSE_VERSION}" != "v1" ]; then
323+
apt-get -y install --no-install-recommends moby-compose || echo "(*) Package moby-compose (Docker Compose v2) not available for OS ${ID} ${VERSION_CODENAME} (${architecture}). Skipping."
324+
fi
322325
else
323326
buildx=()
324327
if [ "${INSTALL_DOCKER_BUILDX}" = "true" ]; then
325328
buildx=(docker-buildx-plugin)
326329
fi
327-
apt-get -y install --no-install-recommends ${cli_package_name}${cli_version_suffix} "${buildx[@]}" docker-compose-plugin
330+
#install cli + buildx first
331+
apt-get -y install --no-install-recommends ${cli_package_name}${cli_version_suffix} "${buildx[@]}"
332+
333+
# Backward compatibility: Older Docker CE versions bundled buildx with CLI
334+
# Modern versions have separate packages, but this ensures consistent behavior
328335
buildx_path="/usr/libexec/docker/cli-plugins/docker-buildx"
329-
# Older versions of Docker CE installs buildx as part of the CLI package
330336
if [ "${INSTALL_DOCKER_BUILDX}" = "false" ] && [ -f "${buildx_path}" ]; then
331-
echo "(*) Removing docker-buildx installed from docker-ce-cli since installDockerBuildx is disabled..."
337+
echo "(*) Removing docker-buildx (bundled in older Docker CE) since installDockerBuildx is disabled..."
332338
rm -f "${buildx_path}"
333339
fi
340+
341+
if [ "${DOCKER_DASH_COMPOSE_VERSION}" != "v1" ]; then
342+
apt-get -y install --no-install-recommends docker-compose-plugin
343+
fi
334344
fi
335345
unset buildx buildx_path
336346
fi
337-
338347
# If 'docker-compose' command is to be included
339348
if [ "${DOCKER_DASH_COMPOSE_VERSION}" != "none" ]; then
340349
case "${architecture}" in
@@ -438,6 +447,8 @@ echo "docker-init doesn't exist, adding..."
438447

439448
# By default, make the source and target sockets the same
440449
if [ "${SOURCE_SOCKET}" != "${TARGET_SOCKET}" ]; then
450+
# Create parent directory if it doesn't exist
451+
mkdir -p "$(dirname "${SOURCE_SOCKET}")"
441452
touch "${SOURCE_SOCKET}"
442453
ln -s "${SOURCE_SOCKET}" "${TARGET_SOCKET}"
443454
fi

test/docker-outside-of-docker/docker_dash_compose_v1.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ source dev-container-features-test-lib
77

88
# Definition specific tests
99
check "docker-compose" bash -c "docker-compose --version | grep -E '1.[0-9]+.[0-9]+'"
10+
check "no docker compose plugin" bash -c "if command -v docker >/dev/null 2>&1; then ! docker compose version >/dev/null 2>&1; else true; fi"
1011

1112
# Report result
1213
reportResults
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
# Test script to assert root Docker socket usage
3+
4+
if [ ! -S "/var/run/docker-host.sock" ]; then
5+
echo "ERROR: Root Docker socket not found"
6+
exit 1
7+
fi
8+
9+
echo "Root Docker detected"
10+
export DOCKER_HOST="unix:///var/run/docker-host.sock"
11+
docker --version
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/bin/bash
2+
set -e
3+
4+
source dev-container-features-test-lib
5+
6+
echo "=== Rootless Docker Socket Configuration Test ==="
7+
8+
# Test the custom rootless socket path
9+
EXPECTED_SOCKET="/var/run/docker-rootless.sock"
10+
11+
# Check if the configured rootless socket exists and is accessible
12+
check "rootless-socket-exists" test -S "$EXPECTED_SOCKET"
13+
check "rootless-socket-readable" test -r "$EXPECTED_SOCKET"
14+
15+
# Verify Docker functionality using the rootless socket
16+
export DOCKER_HOST="unix://$EXPECTED_SOCKET"
17+
check "docker-functional-rootless" docker ps >/dev/null
18+
19+
# Test basic Docker operations with rootless configuration
20+
check "docker-version-rootless" docker version --format '{{.Client.Version}}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+' >/dev/null
21+
check "docker-info-rootless" docker info >/dev/null
22+
23+
# Demonstrate that customers can configure custom socket paths
24+
echo "Configured rootless socket path: $EXPECTED_SOCKET"
25+
echo "Docker host: $DOCKER_HOST"
26+
27+
reportResults

test/docker-outside-of-docker/scenarios.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,32 @@
180180
"moby": false
181181
}
182182
}
183+
},
184+
"rootless_docker_socket": {
185+
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
186+
"features": {
187+
"docker-outside-of-docker": {
188+
"moby": false,
189+
"socketPath": "/var/run/docker-rootless.sock"
190+
}
191+
},
192+
"mounts": [
193+
{
194+
"source": "/var/run/docker.sock",
195+
"target": "/var/run/docker-rootless.sock",
196+
"type": "bind"
197+
}
198+
],
199+
"containerUser": "vscode"
200+
},
201+
"root_docker_socket": {
202+
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
203+
"features": {
204+
"docker-outside-of-docker": {
205+
"moby": false
206+
}
207+
},
208+
"containerUser": "vscode"
183209
},
184210
"docker_dash_compose_latest_moby": {
185211
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
@@ -202,3 +228,5 @@
202228
"containerUser": "vscode"
203229
}
204230
}
231+
232+

0 commit comments

Comments
 (0)