Access your host's Docker install from inside a dev container. Installs Docker extension in the container along with needed CLIs.
Metadata | Value |
---|---|
Contributors | The VS Code team |
Categories | Other |
Definition type | Dockerfile |
Works in Codespaces | Yes |
Container host OS support | Linux, macOS, Windows |
Container OS | Debian (though Ubuntu could be used instead) |
Languages, platforms | Any |
Note: There is also a Docker Compose variation of this same definition. If you need to mount folders within the dev container into your own containers, you may find Docker in Docker meets your needs better, though with a potential performance penalty.
Dev containers can be useful for all types of applications including those that also deploy into a container based-environment. While you can directly build and run the application inside the dev container you create, you may also want to test it by deploying a built container image into your local Docker Desktop instance without affecting your dev container.
This example illustrates how you can do this by running CLI commands and using the Docker VS Code extension right from inside your dev container. It installs the Docker extension inside the container so you can use its full feature set with your project.
Note: If preferred, you can use the related docker/moby install script in your own existing Dockerfiles instead.
The included .devcontainer/Dockerfile
can be altered to work with other Debian/Ubuntu-based container images such as node
or python
. You'll also need to update remoteUser
in .devcontainer/devcontainer.json
in cases where a vscode
user does not exist in the image you select. For example, to use mcr.microsoft.com/vscode/devcontainers/javascript-node
, update the Dockerfile
as follows:
FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:14
...and since the user in this container is node
, update devcontainer.json
as follows:
"remoteUser": "node"
While recommend just using docker/moby script from the script library as an easy way to get this running in your own existing container, this section will outline the how you can selectively add this functionality to your own Dockerfile in two parts: enabling access to Docker for the root user, and enabling it for a non-root user.
You can adapt your own existing development container Dockerfile to support this scenario when running as root by following these steps:
-
First, install the Docker CLI in your container. From
.devcontainer/Dockerfile
:# Install Docker CE CLI RUN apt-get update \ && apt-get install -y apt-transport-https ca-certificates curl gnupg2 lsb-release \ && curl -fsSL https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/gpg | apt-key add - 2>/dev/null \ && echo "deb [arch=amd64] https://download.docker.com/linux/$(lsb_release -is | tr '[:upper:]' '[:lower:]') $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list \ && apt-get update \ && apt-get install -y docker-ce-cli # Install Docker Compose RUN LATEST_COMPOSE_VERSION=$(curl -sSL "https://api.github.com/repos/docker/compose/releases/latest" | grep -o -P '(?<="tag_name": ").+(?=")') \ && curl -sSL "https://github.com/docker/compose/releases/download/${LATEST_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose \ && chmod +x /usr/local/bin/docker-compose
-
Then just forward the Docker socket by mounting it in the container using the
mounts
property. From.devcontainer/devcontainer.json
:"runArgs": ["--init"], "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ]
While technically optional,
--init
enables an init process to properly handle signals and ensure Zombie Processes are cleaned up. -
Press F1 and run Remote-Containers: Rebuild Container so the changes take effect.
This can be a bit trickier than it might first seem if you're looking to ensure things run locally on macOS, Windows, and Linux as well as in Codespaces. The docker script used in this container automatically detects the right thing to do to enable this scenario, but it uses the following two approaches to accomplish it.
In short, you can ignore this if you use the script, but here's what it does.
In some environments like Codespaces, this is relatively simple to achieve if the Docker socket already has a group other than root on it. To see if this is the case, open a terminal in VS Code when connected to the container to check:
stat -c '%g' /var/run/docker.sock
If you get a number other than 0
, you can simply add your non-root user to right user group. To do so:
-
As before, follow the instructions in the Remote - Containers documentation to create a non-root user with sudo access if you do not already have one (though sudo is not required if you start the container itself as root as shown here).
-
Follow the directions in the section on root access to install the Docker CLI.
-
Update your
devcontainer.json
from above so VS Code doesn't override the container's entrypoint and enables the non-root user:"runArgs": ["--init"], "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ] "remoteUser": "vscode", "overrideCommand": false
-
Next, update your Dockerfile as follows to wire up an entrypoint that creates a group with the right group ID and be sure the user is in it:
ARG NONROOT_USER=vscode RUN echo "#!/bin/sh\n\ sudoIf() { if [ \"\$(id -u)\" -ne 0 ]; then sudo \"\$@\"; else \"\$@\"; fi }\n\ SOCKET_GID=\$(stat -c '%g' /var/run/docker.sock) \n\ if [ \"${SOCKET_GID}\" != '0' ]; then\n\ if [ \"\$(cat /etc/group | grep :\${SOCKET_GID}:)\" = '' ]; then sudoIf groupadd --gid \${SOCKET_GID} docker-host; fi \n\ if [ \"\$(id ${NONROOT_USER} | grep -E \"groups=.*(=|,)\${SOCKET_GID}\(\")\" = '' ]; then sudoIf usermod -aG \${SOCKET_GID} ${NONROOT_USER}; fi\n\ fi\n\ exec \"\$@\"" > /usr/local/share/docker-init.sh \ && chmod +x /usr/local/share/docker-init.sh # VS Code overrides ENTRYPOINT and CMD when executing `docker run` by default. # Setting the ENTRYPOINT to docker-init.sh will configure non-root access to # the Docker socket if "overrideCommand": false is set in devcontainer.json. # The script will also execute CMD if you need to alter startup behaviors. ENTRYPOINT [ "/usr/local/share/docker-init.sh" ] CMD [ "sleep", "infinity" ]
-
Press F1 and run Remote-Containers: Rebuild Container so the changes take effect.
However, if the host's socket is owned by the root user and root group (root
root
), you'll need to either change the group on the socket on the host or use socat
to proxy the Docker socket without affecting its permissions. The socat
option can be safer than updating the permissions of the host socket itself since this would apply to all containers. You can also alias docker
to be sudo docker
in a .bashrc
file, but this does not work in cases where the Docker socket is accessed directly.
Follow these directions to set up non-root access using socat
:
-
Follow the instructions in the Remote - Containers documentation to create a non-root user with sudo access if you do not already have one (though sudo is not required if you start the container itself as root as shown here).
-
Follow the directions in the section on root access to install the Docker CLI.
-
Update your
devcontainer.json
to mount the Docker socket todocker-host.sock
in the container and ensure VS Code enables the non-root user, but does not override the entrypoint:"runArgs": ["--init"], "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker-host.sock,type=bind" ], "remoteUser": "vscode", "overrideCommand": false
-
Next, add the following to your
Dockerfile
to wire upsocat
:ARG NONROOT_USER=vscode # Default to root only access to the Docker socket, set up non-root init script RUN touch /var/run/docker-host.sock \ && ln -s /var/run/docker-host.sock /var/run/docker.sock \ && apt-get update \ && apt-get -y install socat # Create docker-init.sh to spin up socat RUN echo "#!/bin/sh\n\ sudoIf() { if [ \"\$(id -u)\" -ne 0 ]; then sudo \"\$@\"; else \"\$@\"; fi }\n\ sudoIf rm -rf /var/run/docker.sock\n\ ((sudoIf socat UNIX-LISTEN:/var/run/docker.sock,fork,mode=660,user=${NONROOT_USER} UNIX-CONNECT:/var/run/docker-host.sock) 2>&1 >> /tmp/vscr-docker-from-docker.log) & > /dev/null\n\ \"\$@\"" >> /usr/local/share/docker-init.sh \ && chmod +x /usr/local/share/docker-init.sh # VS Code overrides ENTRYPOINT and CMD when executing `docker run` by default. # Setting the ENTRYPOINT to docker-init.sh will configure non-root access to # the Docker socket if "overrideCommand": false is set in devcontainer.json. # The script will also execute CMD if you need to alter startup behaviors. ENTRYPOINT [ "/usr/local/share/docker-init.sh" ] CMD [ "sleep", "infinity" ]
-
Press F1 and run Remote-Containers: Rebuild Container so the changes take effect.
That's it!
Note: If you need to mount folders within the dev container into your own containers using docker-from-docker, so you may find Docker in Docker meets your needs better in some cases (despite a potential performance penalty).
In some cases, you may want to be able to mount the local workspace folder into a container you create while running from inside the dev container (e.g. using -v
from the Docker CLI). The issue is that, with "Docker from Docker", containers are always created on the host. So, when you bind mount a folder into any container, you'll need to use the host's paths.
In GitHub Codespaces, the workspace folder is available in the same place on the host as it is in the container, so you can bind workspace contents as you would normally.
However, for Remote - Containers, this is typically not the case. A simple way to work around this is to put ${localWorkspaceFolder}
in an environment variable that you then use when doing bind mounts inside the container.
Add the following to devcontainer.json
:
"remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }
Then reference the env var when running Docker commands from the terminal inside the container.
docker run -it --rm -v "${LOCAL_WORKSPACE_FOLDER//\\/\/}:/workspace" debian bash
There are no special setup steps are required, but note that the included .devcontainer/Dockerfile
can be altered to work with other Debian/Ubuntu-based container images such as node
or python
. Just, update the FROM
statement to reference the new base image. For example, you could use the pre-built mcr.microsoft.com/vscode/devcontainers/python:3
image:
FROM mcr.microsoft.com/vscode/devcontainers/python:3
Beyond that, just follow these steps to use the definition:
-
If this is your first time using a development container, please see getting started information on setting up Remote-Containers or creating a codespace using GitHub Codespaces.
-
Start VS Code and open your project folder or connect to a codespace.
-
Press F1 select and Add Development Container Configuration Files... command for Remote-Containers or Codespaces.
Note: If needed, you can drag-and-drop the
.devcontainer
folder from this sub-folder in a locally cloned copy of this repository into the VS Code file explorer instead of using the command. -
Select this definition. You may also need to select Show All Definitions... for it to appear.
-
Finally, press F1 and run Remote-Containers: Reopen Folder in Container or Codespaces: Rebuild Container to start using the definition.
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. See LICENSE.