Tutorial
Create portable dev environments with Dev Containers
Learn how to set up and use Dev Containers for a consistent and portable development environmentWhen developing software, the first step before writing any code is to set up your development environment. Depending on your team, you may have multiple developers with different devices, operating systems, and versions of software already installed on their machines. This variation can make it challenging to keep your team’s environments in sync, especially if you are managing multiple applications using a variety of different languages and technologies. Dev Containers help create a uniform development environment that can be shared and managed via source control.
What is containerized development?
Containerized development involves developing your software applications entirely inside a container or a set of containers. This usually involves creating a shared volume between your host file system and the container so that changes you make to files persist on your host machine. If you are unfamiliar with container technology or how it works, check out Docker’s explanation, Use containers to Build, Share and Run your applications.
In this article, Dev Containers refers to the Visual Studio Code extension that helps you manage developing inside containers. For a more detailed explanation of containerized development and the features provided by the VS Code Dev Containers extension, check out their Developing inside a container guide.
Why develop inside containers?
Dev Containers offer several advantages:
- Allow you and your team to share the same development environment and VS Code extensions.
- Use the same container base images that your applications run on in production.
- Help keep installed software on your machine to a minimum.
- Eliminate the need to manage multiple versions of languages and dependencies using software like nvm or pyenv.
- Make it easier to switch contexts when maintaining a suite of applications that use different technologies.
- Reduce the time to onboard new developers to your projects.
Setting Up Your Project with Dev Containers
Prerequisites
Before starting this tutorial, ensure you have a basic understanding of containers and have the following software installed:
- Visual Studio Code
- One of the following containerization software:
- A PostgreSQL database client: DBeaver
Steps
Step 1. Install the Visual Studio Code dev container extension and create a project directory
Install the Dev Containers extension:
- Open Visual Studio Code.
- Click on the Extensions tab.
- Search for the "Dev Containers" extension and install it.

Step 2. Configure the extension
If you are using Podman or another containerization software besides Docker, you may need to make some slight changes to the Dev Container configuration settings.
Go to the Extensions tab in VS Code and click on the extension settings for the newly installed Dev Containers extension.

Scroll to the
Docker Compose PathandDocker Pathfields.
Enter the path to your Podman and Podman Compose executables.
Step 3. Create Dev Container configuration files
Open the Command Palette:
In VS Code, type Ctrl+Shift+P (Windows/Linux) or Cmd+Shift+P (Mac) to open the Command Palette.
Search for
Add Dev Container Configuration Files.

Add Configuration:
Hit Enter and click
Add Configuration to Workspace.
Type
Node.js & PostgreSQLand hit Enter again.
Select
Node 20 Bookworm (latest stable base image)and hit Enter one more time.
This will create a new .devcontainer folder in your project with the following files:
devcontainer.json: A configuration file used by VS Code to help manage your dev container. You can find all the possible configuration values at Specification reference.
docker-compose.yml: A Docker Compose file that will be used to build out your development container stack. This includes the container you will be developing in and any supporting services like databases, caches, search engines, etc. For more information, see Docker Compose overview.
Dockerfile: The Dockerfile used to build your dev container image.
Step 4. Running the Dev Container
Rebuild and Reopen in Container:
- Pull up the Command Palette (Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac).
Type
Rebuild and Reopen in Containerand hit Enter.
Build and Spin Up Containers: This will begin to build your container image from your Dockerfile and spin up any other containers in your Docker Compose stack.
You can find more information about Dev Container Command Palette and CLI commands at Dev Container CLI.
Step 5. Running commands in the Dev Container
Once the container spins up, you can begin developing in it.
Open a Terminal:
- Click the
+button on the bottom right-hand side of your code editor or use Ctrl + backtick (`) to open a terminal.
- Click the
This terminal will execute commands directly inside your container, not on your host machine. You will only have access to commands and programs installed via your Dockerfile.
If you need to run commands on your host machine, see the tips section below.
Step 6. Developing in the Container
Now that we have a working dev container, let's create a test application.
Create a New Application: In your newly spawned terminal, run the command:
npx create-next-app@latestThis will guide you through the steps of creating a new Next.js application and installing any necessary dependencies.
To navigate to your project folder, run
cd <your-project-folder>To start your application, run:
npm run devThis will spin up your Next.js app.
Navigate to http://localhost:3000 to see your running application.
Step 7. Connect to Your Postgres Database
In your
docker-compose.ymlfile, add the following lines to your db service:expose: - 5432 ports: - 5432:5432These lines will expose the port the database is running on and map it to your host machine.
Rebuild the Container:
Open the Command Palette (Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac).
Run the
Rebuild Containercommand.
Anytime you make changes to one of the configuration files inside the
.devcontainerdirectory, you will need to rebuild the container for the changes to take effect.
Connect to the Database:
- Open your preferred PostgreSQL database client.
- Connect to the database running on
localhost:5432. The username, password, and database should all be set to
postgres.
You can also set your own values in the
docker-compose.ymlfile generated by VS Code.db: environment: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgres POSTGRES_DB: postgresCongratulations! You have just set up your very first containerized development environment.
Useful Dev Container tips
Here are some helpful tips that can make developing inside dev containers a little easier:
Increase container resource allocation
Since you will be doing all of your development work inside your containers, it might be a good idea to increase the amount of CPU cores, memory, and storage allocated to them. You can do this through your containerization software's GUI or CLI:
Docker desktop (GUI):
preferences > advanced

Rancher (GUI):
preferences > virtual machine

Podman (CLI):
podman machine stop
podman machine set -cpus=x --memory=y --disk-size=z
podman machine start
Add a post create command
To automate your dev environment’s build step, you can create a post-create command in your devcontainer.json. This command will run as soon as the dev container is created for the first time. It is a great place to install dependencies through a package manager like pip, npm, or yarn.
Example:
"postCreateCommand": "npm install",
Speed up NPM installations
If you are experiencing slow NPM installations, it is recommended to create a separate targeted named volume to store your node modules. This ensures that the node modules you install do not persist in the same location on your host machine.
Example:
app:
volumes:
- app-node-modules:/workspaces/app/client/node_modules
volumes:
app-node-modules
To delete the contents of the installed node modules, simply delete the volume:
docker volume rm app-node-modules
For more information, refer to the Visual Studio Code documentation on improving container performance.
Slow jest test cases and unexpected crashes
If your Jest test cases are running slower than usual inside the container or are causing your containers to crash, try the following solutions:
Run Tests In-Band:
jest --runInBandManually Set the Max Number of Workers:
jest --maxWorkers=4
For more details, refer to this Stack Overflow discussion on running Jest tests in Docker.
Webpack file watcher limit exceeded
When developing inside a container using webpack dev server or a similar program that monitors files in your project for changes, you might encounter the following error:
Error: ENOSPC: System limit for number of file watchers reached
To fix this error, you can try the following solutions:
Increase the file watcher Limit:
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -pIgnore the
node_modulesFolder in yourwebpack.config.jsfile:watchOptions: { ignored: /node_modules/, }
The node_modules folder could have thousands of files that are not likely to change during the course of development.
For more details, refer to this Stack Overflow discussion on system limits for file watchers.
Container networking
If you want your dev container to communicate with other containers defined in your docker-compose.yml, you may want to set up container networking. When using container networking, your service’s hostnames will be their container names.
Example configuration in docker-compose.yml:
services:
app:
container-name: app
networks:
- devnetwork
db:
container-name: app-db
networks:
- devnetwork
networks:
devnetwork:
name: devnetwork
driver: bridge
In this setup, the app and db containers can communicate with each other using their respective container names (app and app-db).
Spawn a terminal on your host machine
Sometimes you will want to spawn a terminal on your host machine instead of inside your dev container. You can easily do this in VS Code by opening the command palette (ctrl+shift+p or cmd+shift+p) and searching for Create New Integrated Terminal (local).
Conclusion
After completing this tutorial, you should see the benefits of developing inside containers and consider adopting containerized development for your own projects. This approach provides a portable development environment that you can share with your team via source control, running on most operating systems with minimal setup.