I already have done a post on how to create a complete Devcontainer using Devpod and Neovim. See reference here.
Recently I was challenged on creating the same setup but for other code editors like VSCode or Intellij. And since I spent some more time on it, I thought it would be also a good idea to explore more complex setups using Devcontainers (like for instance integrating a custom Dockerfile or a custom docker-compose file).
Devcontainers and VSCode/Intellij
Both VSCode and Intellij support Devcontainers through plugins (on Intellij it is even pre-installed). So, the only thing to do is to install the Devcontainer plugin on VSCode if use this text editor and you are good to go.
Setting up the devcontainer
In order to reproduce my setup, I will detail step by step how I did it from scratch and we will complexify the setup as we go.
Step 1: Create a simple devcontainer and test it
In this first step, we will:
- Create an empty project directory.
- Create a very simple devcontainer that we can start in VSCode or Intellij.
Execute the following commands to create your project directory:
mkdir -p test-devcontainer/.devcontainer
touch test-devcontainer/.devcontainer/devcontainer.json
Open the test-devcontainer
directory in VSCode (or Intellij) and add the following content to devcontainer.json
:
{
"name": "test-devcontainer",
"image": "mcr.microsoft.com/devcontainers/base:debian"
}
You can launch the devcontainer build like so:
- In VSCode, type CTRL+SHIFT+P > Rebuild and open in dev container (use CMD+SHIFT+P if you use MacOS).
- In Intellij, right clic on the
devcontainer.json
file > Dev containers > Create Dev Container and mount sources > Intellij IDEA.
A new container will be built and your IDE window will automatically switch to your new devcontainer.
Step 2: Add a custom Dockerfile
In the Devcontainer spec it is mentionned that you can reference a custom Dockerfile tailored to your needs (in order to install software for example). Lets do that:
In this first step, we will:
- Create a custom Dockerfile.
- Modify our devcontainer definition so it is read.
In order to do that, we need to create the Dockerfile:
touch test-devcontainer/.devcontainer/Dockerfile
And modify our devcontainer.json
to read it:
{
"name": "test-devcontainer",
"build": {
"dockerfile": "Dockerfile"
}
}
And finally, add the following content to your Dockerfile (feel free to adapt the dependencies to your needs):
FROM mcr.microsoft.com/devcontainers/base:debian
# add dependencies
RUN apt update && \
apt install -y software-properties-common apt-transport-https ca-certificates gnupg git tree curl wget bat gpg ripgrep stow cmake tmux fd-find direnv zip unzip
# tell debian to use bash as default shell
RUN ln -snf /bin/bash /bin/sh
Finally, rebuild the container in VSCode or Intellij like we did before. Opening a terminal should allow you to execute any command using the installed software.
Step 3: Add a custom docker-compose
If you want to deploy a full development environment, you will probably need more than just one container (like for instance your app and a database). Here is how to do it:
In this step, we will:
- Create a docker-compose file with two services.
- Modify our devcontainer configuration to use it.
in order to do that, we need to create a custom docker-compose file:
touch test-devcontainer/.devcontainer/docker-compose.yaml
Add the following content to this file:
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- ..:/workspace/vscode
depends_on:
- db
db:
image: postgres:latest
restart: unless-stopped
ports:
- 5432:5432
volumes:
- postgres-data:/var/lib/postgresql-data
environment:
POSTGRES_DB: postgres
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
volumes:
postgres-data:
Very basic, this file creates an app container using your Dockerfile and spins up another container containing a postgresql database.
Next, modify your devcontainer.json
file to handle the docker-compose instead of the Dockerfile:
{
"name": "test-devcontainer",
"dockerComposeFile": "docker-compose.yaml",
"service": "app",
"workspaceFolder": "/workspace/vscode"
}
The service directive indicates which is the main container your IDE will access (here the app), and the workspace folder in which directory the filesystem will be mounted (usually /workspace/vscode).
Finally, we also need to modify our Dockerfile like so:
FROM mcr.microsoft.com/devcontainers/base:debian
# add dependencies
RUN apt update && \
apt install -y software-properties-common apt-transport-https ca-certificates gnupg git tree curl wget bat gpg ripgrep stow cmake tmux fd-find direnv zip unzip
# tell debian to use bash as default shell
RUN ln -snf /bin/bash /bin/sh
# Create the workspace directory
RUN mkdir -p /workspace/vscode
WORKDIR /workspace/vscode
# Keep the container running
CMD ["tail", "-f", "/dev/null"]
We modified this file for the following:
- Create the workspace directory declared in
devcontainer.json
. - Declare this directory as the default workdir.
- Make the container loop waiting for instructions (without this last command, the container will start and immediately exit).
Just like before, you can rebuild the devcontainer in VSCode or Intellij. You should see the following:
- Opening a terminal gives you access to the main service (here app).
- You can access the second container with the database by doing the following:
sudo docker ps
This should give you the name of the container running the database. You can then connect to it using the command:
sudo docker exec -it <container_name> bash
Step 4: (Bonus) execute a custom script
You can even go further in your setup and execute a custom script once the container is created. This could allow you to install third part software for example.
In order to do that, you need to:
- Create the custome script.
- Modify your
devcontainer.json
in order to take it into account.
First, lets create a file for the custom script:
touch test-devcontainer/.devcontainer/install-software.sh
chmod +x test-devcontainer/.devcontainer/install-software.sh
Add the following content to this new file (for example):
#!/bin/sh
curl -s https://get.sdkman.io | bash
source $HOME/.sdkman/bin/sdkman-init.sh
sdk install java 24-zulu
sdk install grails 6.2.3
Here, I needed to install sdkman to handle my java and grails installations.
Next, modify the devcontainer.json
file as follows:
{
"name": "test-devcontainer",
"dockerComposeFile": "docker-compose.yaml",
"service": "app",
"workspaceFolder": "/workspace/vscode",
"postCreateCommand": "./.devcontainer/install-software.sh"
}
We added a postCreateCommand instruction pointing to our newly created script. When building the container, this script will be executed as last instruction.
Conclusion
In this article, I started from scratch and showed how one can create a complete devcontainer environment using Docker, docker-compose and custom scripts.
This devcontainer can be built using IDEs like VSCode or Intellij which support devcontainers out of the box.
Leave a Reply