Developing or debugging code inside Docker containers can be tricky. You’d usually attach to the container and use shell utilities to edit files. Visual Studio Code’s Remote Containers extension lets you open folders inside containers, so you can work with them in your code editor.
Remote development works with Docker Desktop 2.0 and up for Windows and macOS and Docker Engine 18.06 and higher for Linux. If you’re using Windows 10 Home, you must have the April 2020 update, Docker Desktop 2.3, and the Windows Subsystem for Linux installed.
On the container side, VS Code is compatible with both x64 and ARM containers. You can use Alpine, Debian, Ubuntu, CentOS and RHEL operating systems. If your base image doesn’t work out of the box, you should be able to install additional packages inside it to complete the connection.
Getting Started
Launch VS Code, press Ctrl+Shift+P and search for “extensions”. Select the “Install Extensions” item to bring up the sidebar. Within the extensions pane, search for “Remote – Containers” and install the matching item.
After the extension installs, a new green button will appear in the bottom-left of your status bar. Click this to bring up the command palette, prefilled with available remote commands. If you’d rather avoid using the mouse, you can also find the commands by pressing Ctrl+Shift+P and typing “remote”. The extension will surface in other areas of the UI too, such as the welcome page and folder opening pane.
To open your first Dockerized folder, bring up the command list and select “Attach to Running Container”. You’ll need to acknowledge a warning that the container could execute untrusted code. You should only attach to containers created from images you trust, as a malicious container with a VS Code workspace file could compromise your system.
VS Code will display a list of the Docker containers running on your system. Select the container you want to connect to. A new window will launch. It might take a few seconds to start while VS Code installs its remote server components into the container. You’ll see the active container displayed in the bottom-left of the status bar.
Once the window’s ready, you can begin work. Click the “Open Folder” button in the left pane to bring up a file picker prompt. This displays the filesystem within the container. Navigate to the directory you’d like to open and press “OK”.
The sidebar will update to display the selected directory’s contents. Click any of the files to open it in the VS Code editor. You can now make changes inside the container, without manually copying files or setting up a working directory bind mount. This maximizes efficiency when using a Dockerized development environment or debugging a malfunctioning container.
VS Code’s full feature set is available, including IntelliSense code completion and its debugging toolkit. Panes such as the console and terminal will attach to the container’s input and output streams.
How Does It Work?
The first thing to recognize about the integration is that VS Code will alter your container’s filesystem. It’ll install a server in the containers you connect to, which feeds information back to the editor client.
The server monitors the filesystem, reads files, and launches processes within the container. This powers capabilities like VS Code’s debugger. The editor will ask the server to run your source, letting it inspect code running inside the container.
Similarly, VS Code will install copies of your others extensions inside the container. This ensures they’ve got full access to the container filesystem, so they’ll operate with no discrepancies compared to a local folder. The end result is a fully-fledged editor experience that operates transparently, even though it’s spread across your host and containers. Depending on the size of your environment, first-run setup could take a few moments but the container server will be cached for subsequent use.
Development Containers
Beyond opening files in an existing container, the remote system lets you define development containers that encapsulate your working environment. Such a container should include all the software dependencies you’d install before developing your application locally.
When you’re using a development container, you can either bind mount your working directory or use an isolated volume. The latter option avoids filesystem pollution and offers improved performance as it’s more native to Docker.
Development containers are created from .devcontainer/devcontainer.json files. These describe the Docker and Visual Studio configuration that launches your development environment.
The devcontainer.json file shown above sets up a development container that uses Microsoft’s Node.js template. The EditorConfig VS Code extension is enabled, adding support for .editorconfig files in your working directory. Port 3000 is then mapped from the host into the container.
Dev container files support a few other options too. You can set VS Code settings.json values, add a command to run inside the container, and define the user that the VS Code server will run as.
If you don’t want to create a container config file yourself, the Remote-Containers: Add Development Configuration Files command will add one to your project automatically. You’ll be able to select from a list of pre-configured templates which you can customize later on.
Once you’ve got a devcontainer.json file in your project, run the Remote-Containers: Rebuild Container command from the command palette. This will build a container image using the specified configuration. When the build completes, run Remote-Containers: Reopen Folder in Container to open your current local directory within a new development container.
If you’re using a Git repository, it’s usually simpler to clone the repo directly into a container. The Remote-Containers: Clone Repository in Container Volume command accepts a Git URL to clone. It’ll create a new volume and attach it to a dev container instance. This lets you work on Git repos without cloning them to your local filesystem, avoiding unnecessary pollution. VS Code automatically shares your local Git credentials with the container.
Configuring the Extension
The Remote – Containers extension comes with several settings that let you configure your container experience. You can find them with the Remote-Containers: Settings for Remote-Containers command.
The first collection of settings concerns dotfiles. Dotfiles refer to configuration files which start with a dot (.). Remote-Containers can automatically clone a Git repository of dotfiles into new containers, helping you get running with your usual settings.
Add your dotfiles repository URL to the Repository setting. The repository will be cloned to the Target Path path, which defaults to ~/dotfiles. If you want to run a command after the repository is cloned, specify it as Install Command.
Remote-Containers defaults to using docker as the binary which executes container commands. You can change this to any Docker CLI-compatible binary with the Docker Path setting. If you’re using Podman, specifying podman as this value will let you work with its containers using VS Code.
You can configure default remote extensions under the Default Extensions heading. Click “Add Item” to pick an extension to add to every container. This lets you ensure global availability of your must-use extensions, even if they’re not listed in a devcontainer.json file.
A final group of settings concern Git configuration. When “Copy Git Config” is checked, your local .gitconfig file will automatically copied into containers automatically. Enabling this ensures your commits are correctly attributed to the user details you’re already using locally.
The “Git Credential Helper Config Location” setting controls the config file that new Git credentials will be written to. You can select from your local per-user file, ~/.gitconfig, or the global system location, /etc/gitconfig.
Summary
The Remote – Containers extension for Visual Studio Code lets you edit files and folders inside Docker containers. It works seamlessly with the VS Code editor features, including IntelliSense, directory indexing, debugging, and extensions. Internally, VS Code launches a development server inside the container so its tools have full access to the filesystem they’re working with.
There are several possible use cases for this functionality, with Dockerized development environments and on-the-fly container edits the top contenders. While the idea of using containers to systematize development has been around for a while, VS Code makes it much easier to get started by offering an IDE experience that natively understands Docker.
You could prepare a Docker image that gives developers everything they need to work on your system – programming language, dependencies, and convenience tools. Devs would start a container, connect to it from VS Code, and go about their work without installing anything (except Code) on their own machine. If a dependency has to be patched, you only need to update the shared Docker image and get developers to pull the new tag.