VOOZH about

URL: https://blog.logrocket.com/using-dev-containers-vs-code-easier-dev-setup/

โ‡ฑ Using dev containers with VS Code for an easier dev setup - LogRocket Blog


2023-11-21
3260
#docker
Yashodhan Joshi
181937
109
๐Ÿ‘ Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

In todayโ€™s software architecture, containers have become almost indispensable. They play a major role in almost all modern, production-level projects.

๐Ÿ‘ Using Dev Containers In Vs Code For An Easier Dev Setup Process

The flexibility and usability of containers can be further extended to set up a dev environment quickly and without hassle. In this post, we will explore the concept of dev containers and walk through how to set up and use dev containers with VS Code.

To get the most out of this article, you should have some basic familiarity with dev containers, Docker, and running Docker containers. Letโ€™s start with a brief recap of what exactly dev containers are and what benefits they bring to our dev environment setup.

๐Ÿš€ Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

What are dev containers, and why are they useful?

Containers are a very malleable tool, allowing developers to essentially โ€œship their computerโ€ to the users. Their builds can be completely reproducible, they are more lightweight than a full-blown virtual machine (VM), and popular containerization tools like Docker are easy and convenient to use.

Dev containers are a type of container used specifically for development purposes. You can use them to set up your development environment or to run the tests in a specific environment.

Many commercial developer platforms support dev containers, such as GitHub and GitPod. This provides you with a cloud-based developer environment that you can set up with a single configuration file and a click.

When setting up a container build using a tool like Docker, you specify all the setup instructions โ€”configurations, package dependencies, etc. โ€” in the Dockerfile. This makes the container build completely reproducible โ€” you only have to set up Docker correctly, and then you can manage the rest of the setup via the Dockerfile.

Dev containers can be especially useful for monorepos containing multiple projects using multiple programming languages and dependencies. Consider a typical dev setup process for this kind of project โ€” you would need to:

  • Install and set up each programming language with the appropriate compiler/interpreter and package manager
  • Set up and populate your database if youโ€™re using one
  • Ensure that the system-level packages and dependencies are correctly set up and that their versions match whatโ€™s required for the development
  • Set up any project-specific needs, such as config files, .env files, or anything else

In other words, you have to spend a lot of time fiddling with the setup before you can start any actual development, not to mention the time spent debugging the setup itself if you experience any errors. You can easily avoid this cumbersome manual setup by using dev containers.

Setting up and using a dev container with VS Code

VS Code has some great extensions that allow us to use this popular code editor along with dev containers. Letโ€™s go through a hands-on example of how to set up and use dev containers with VS Code.

To get started, create a new directory and open VS Code in it. Open the extensions side panel and install the Remote Development extension pack from the marketplace. This extension allows us to create and connect to dev containers directly from VS Code.

Now in the project directory, create a directory named .devcontainer. Naming your file this way is important, as the name helps VS Code detect the Dockerfiles we want to use for our dev containers.

For our first example, we will set up a Ruby dev environment. Start by creating a Dockerfile in the .devcontainer directory and adding the following code:

FROM ruby:alpine3.18

This will use the official Ruby image from an Alpine base image.

Next, we need to create a devcontainer.json file, which we can use to add various configurations and specify which Dockerfile we want VS Code to use. Create the file in the .devcontainer directory and add the following code:

{
 "name": "Ruby-test",
 "build": {
 "dockerfile": "Dockerfile"
 }
}

Now at the bottom left of your VS Code window, you should see an icon. Hovering over this icon should show a pop-up tooltip that says Open a remote window:

๐Ÿ‘ Popup Tooltip In Vs Code To Open A Remote Window

After clicking on this, it will open the VS Code command palette, where you should see several options related to working with dev containers:

๐Ÿ‘ Vs Code Command Palette Open To Show Options Related To Working With Dev Containers

From this list of options, select Reopen in Container. This can take a second, but VS Code will eventually relaunch. When this happens, a small progress pop-up will be visible at the bottom right of your window:

๐Ÿ‘ Popup Indicating Progress Of Starting Dev Container Process

If you click on show log, it will open a terminal with logs for building the container. Wait for it to finish. After, VS Codeโ€™s standard welcome page will open automatically.


Over 200k developers use LogRocket to create better digital experiences

๐Ÿ‘ Image
Learn more โ†’

Once all this is done, you can open a new terminal in VS Code either by using the + button or using ctr + shift + ~. Your window should now show a command prompt running inside the container.

The command prompt you see should look different from your normal command prompt. Usually, your prompt would be formatted something like the following: username@os-name. However, you should see that it now looks something like /workspaces/project # or root@container-hash instead.

At this point, you can type ruby --version to check that Ruby is indeed installed and set up correctly. Create a directory called ruby-test and a file inside it called test.rb. Add the following code to this file and save it:

puts "Hello Dev-container World!"

Now in the VS Code terminal, you can run ruby ./ruby-test/test.rb and it will print out the string defined above. Once you are done with this, you can again click on the bottom-left icon, which now displays the name of the dev container from the JSON file:

๐Ÿ‘ Bottom Left Icon In Vs Code Window Now Labeled With Dev Container Name From Json File

As before, clicking this will open the command palette. From this palette, select the Close remote connection option:

๐Ÿ‘ Vs Code Command Palette With User Pointer Hovering Over Option To Close Remote Connection

This will close the dev container and open the default VS Code window. Now, if you close this default window and open VS Code in our project directory, you will see that the ruby-test directory and test.rb file are still there.

Congratulations ๐ŸŽ‰ You have created and used your first dev container! As you can see, you can change the files in your dev container, and those changes will be retained in the actual files as well.

Exploring other specification options for dev containers in VS Code

While using dev containers, you might realize that you need some dependency added. You can simply add that in the Dockerfile, save it, and then open the command palette again.

In the command palette, you should see an option labeled Rebuild container. This option will build the container again, adding the new dependency this time as well.

Not only can you continue your development work while making sure that this dependency change is version-tracked, but other developers can also use the added dependency as well.

Apart from specifying the Dockerfile in the devcontainer.json file, you can also use this file to specify other configuration options. One such option is postCreateCommand, which you can use to specify a command to be run in the container after building the container.


More great articles from LogRocket:


The command you specify using postCreateCommand runs only once โ€” after you create the container, and right after the Docker image is built, not upon each start of the container. As a result, you can use this configuration option for other setup steps that need to be done once after building the container.

For example, if your dev container uses MongoDB, you can add a mongodump that contains some basic dev data and use mongorestore to seed the DB from this data. You can also specify a command to be run every time the container starts using postStartCommand .

Using multiple dev containers in a single project

If you have a monorepo with multiple individual projects, each using a different programming language or stack, there are various ways to set up dev containers for this project.

One option is to have a single gigantic container image with dependencies you can use for each project. However, this makes the built image considerably large, and it will need to be rebuilt each time any dependency of any project changes. Moreover, someone working on one part may not need all the other parts.

Another option is to instead split up the dev containers on a per-project basis. You can then just use whichever one is needed at a given time. Letโ€™s see this in action.

First, letโ€™s add a Node dev container alongside the Ruby one we already created. To do so, weโ€™ll need to change our structure a bit by creating a directory named ruby inside the .devcontainer directory. Then, weโ€™ll move the existing Dockerfile and devcontainer.json file inside the new ruby directory.

As a reminder, this was our previous structure:

.
โ”œโ”€โ”€ .devcontainer
โ”‚ โ”œโ”€โ”€ devcontainer.json
โ”‚ โ””โ”€โ”€ Dockerfile
โ””โ”€โ”€ ruby-test
 โ””โ”€โ”€ test.rb

Meanwhile, our new structure will look like this:

.
โ”œโ”€โ”€ .devcontainer
โ”‚ โ””โ”€โ”€ ruby
โ”‚ โ”œโ”€โ”€ devcontainer.json
โ”‚ โ””โ”€โ”€ Dockerfile
โ””โ”€โ”€ ruby-test
 โ””โ”€โ”€ test.rb

Now, create another directory named node in .devcontainer and add a Dockerfile and a devcontainer.json file.

In the Dockerfile, add the following code:

FROM node:20

In the devcontainer.json file, add the following code:

{
 "name": "node-test",
 "build": {
 "dockerfile": "Dockerfile"
 }
}

Now if open the command palette and select the Reopen in container option, the palette will show options for both Node and Ruby:

๐Ÿ‘ Vs Code Command Palette With Option To Reopen In Container Selected, Showing Two Options For Node And Ruby

If you select node-test, it will use the Node Dockerfile, and if you select Ruby-test, it will use the Ruby Dockerfile. This way, we can have multiple independent dev containers that can each be updated without having to rebuild the others.

Using multiple connected dev containers

Not only can we use multiple dev containers in a single project, but we can also use multiple connected dev containers to run connected services. Letโ€™s see how.

In this section, we will create an example that uses a Node.js frontend, a Python backend, and Mongo DB as its database. Since weโ€™re focusing on the dev container setup, we wonโ€™t see the actual code in detail. You can get the code for this demo from this GitHub repo.

To start, weโ€™ll create two directories named frontend and backend in the project directory. In the backend directory, we will create a server.py file. In the frontend directory, we will create an index.html file.

We will also create two Dockerfiles named frontend.Dockerfile and backend.Dockerfile.

Finally, we will create a directory named app-frontend in the .devcontainer directory and create docker-compose.yaml and devcontainer.json in the new app-frontend directory. In the end, our setup will look like so:

.
โ”œโ”€โ”€ .devcontainer
โ”‚ โ”œโ”€โ”€ app-frontend
โ”‚ โ”‚ โ”œโ”€โ”€ devcontainer.json
โ”‚ โ”‚ โ””โ”€โ”€ docker-compose.yaml
โ”‚ โ”œโ”€โ”€ node
โ”‚ โ”‚ โ”œโ”€โ”€ devcontainer.json
โ”‚ โ”‚ โ””โ”€โ”€ Dockerfile
โ”‚ โ””โ”€โ”€ ruby
โ”‚ โ”œโ”€โ”€ devcontainer.json
โ”‚ โ””โ”€โ”€ Dockerfile
โ”œโ”€โ”€ backend
โ”‚ โ””โ”€โ”€ server.py
โ”œโ”€โ”€ frontend
โ”‚ โ””โ”€โ”€ index.html
โ”œโ”€โ”€ frontend.Dockerfile
โ”œโ”€โ”€ backend.Dockerfile
โ””โ”€โ”€ ruby-test
 โ””โ”€โ”€ test.rb

The frontend.Dockerfile should contain the following code:

FROM node:20
RUN npm install -g http-server -y
COPY ./frontend /frontend
WORKDIR /frontend
CMD ["http-server",".","-p","3000"]

The backend.Dockerfile should contain the following code:

FROM python:3.9.18-alpine3.18
RUN pip install flask
COPY ./backend /backend
WORKDIR /backend
# we must specify the host as 0.0.0.0 otherwise cannot access outside container
CMD ["flask","--app","/backend/server.py","run","--host","0.0.0.0"]

This sets up a basic Flask server for the backend and an HTTP server for the frontend to serve HTML files. We are keeping this simple here, but you can set this up with any framework or library of your choosing.

Finally, in the docker-compose.yaml file, we will add the following code:

version: "3.8"
services:
 frontend:
 networks:
 - app_demo
 build:
 dockerfile: "./frontend.Dockerfile"
 context: "../../"
 ports:
 - 3000
 backend:
 networks:
 - app_demo
 build:
 dockerfile: "./backend.Dockerfile"
 context: "../../"
 ports:
 - 5000
 db:
 image: mongo:7.0.2
 volumes:
 - type: volume
 source: dbdata
 target: /var/lib/mongodb
 networks:
 - app_demo
 ports:
 - 27017
networks:
 app_demo:

volumes:
 dbdata:

The code above defines three services: backend, frontend, and db. For each service, we use the Dockerfile of the same name, and use the context as the project directory.

Note that the Dockerfileโ€™s path is taken relative to the context directory. So, even if the compose file is stored at the .devcontainer/app-frontend/docker-compose.yaml directoryโ€™s filepath, the Dockerfile path is simply given as ./frontend.Dockerfile. This is because the context is taken relative to the project root.

In each service, we also exposed ports so that the individual services can connect to each other. Finally, we have specified the services to be connected via a custom network named app_demo.

Specifying the custom network is not strictly necessary, as Docker will expose them on the host network by default. However, this way we also get a dedicated network connecting these three.

We can also optionally specify the ports on the host, via which we can connect to these services using the syntax host:container in the ports array.

Finally, in the devcontainer.json file, add the following code:

{
 "name": "app-frontend",
 "dockerComposeFile":"./docker-compose.yaml",
 "service": "frontend",
 "workspaceFolder": "/frontend",
 "mounts": ["type=bind,source=../../frontend,target=/frontend"],
 "shutdownAction": "stopCompose"
}

In this code, we specify the name for this setup, the Docker Compose file, and most importantly, specify which service in the compose file we want to connect with VS Code. The workspaceFolder specifies which directory inside the container should be opened with VS Code.

We also specify that the frontend directory should be mounted at the /frontend path and that the mount type should be bind. This ensures that any changes done inside the container are reflected on our host system.

After thatโ€™s all done, we can reopen VS Code in a container and select app-frontend, which will set up Docker Compose and open VS Code in the frontend container. Here we can access other services via their names and ports.

For example, if we run curl backend:5000, we will get the default response from our Flask server. This way, the backend can connect to the DB and the frontend can connect to the backend as needed.

You can see the whole code in this GitHub repo.

Using dev containers without VS Code

If you donโ€™t want to use either VS Code or Docker, you can still use dev containers. While VS Code extensions provide an easy way to set up dev containers, these dev containers are ultimately still plain old containers and can be directly used without VS Code.

For example, letโ€™s see how we can accomplish the above setup without using VS Code โ€” although weโ€™ll still be using Docker and not a Docker alternative.

First, we will create a new docker-compose file in the root directory and copy over the contents of the previous file. This by itself is already a valid docker-compose file and only needs slight modifications.

Then, we will need to specify the mount info in this file itself instead of in the dockerconfig.json file. Hereโ€™s how weโ€™ll do so:

...
frontend:
 networks:
 - app_demo
 build:
 dockerfile: "./frontend.Dockerfile"
 context: "."
 volumes:
 - type: bind
 source: ./frontend
 target: /frontend
...

Next, we need to specify the host port in order to access the HTTP server from the host. We will use port 3000 for both and only expose the frontend server, but others can be exposed as well:

...
ports:
 - 3000:3000
...

Now if we run the docker compose up command in a terminal, it should start building and running the containers. Once all the containers are up and running, you can visit localhost:3000 in your browser and see the frontend.

If you want to run any command inside the container, you can use commands such as docker exec to open a shell in the container.

As we have added a bind volume, any changes you make in the files will be reflected inside the container and vice versa. You might want to add nodemon or something similar to watch the file changes.

Another thing to note is that if you create files from inside the container, you might need to set the ownership and permissions of the file correctly. This will ensure you can edit them outside the container properly as well.

In this way, we can also use dev containers without using VS Code.

Drawbacks of dev containers

We have seen how easy setting up and using dev containers is โ€” even when setting up multiple dev containers for a single project in the next section. However, itโ€™s also important to understand some of the challenges that we might face while using dev containers.

They consume a fair amount of resources

If youโ€™re developing on Linux, running containers might not use a lot of CPU or RAM, as they are essentially a process just like any other process.

However, if youโ€™re developing on Windows or Mac, running Docker will consume a considerable amount of resources. Each container running on top of it will also need some more CPU and RAM.

If your machine isnโ€™t very powerful, or you donโ€™t want to allocate resources to running Docker, then a conventional setup would be a better choice instead of using dev containers.

They take up a considerable amount of space

This issue will be present no matter what base OS you use. The container image itself, as well as the dev container, will consume space in your storage.

The image we used above is pretty minimal and uses only around 75 MB. However, it is very common for base images to have sizes of around 500MB up to 1GB.

Apart from the base image, any extra packages you install, along with any commands you add to the Dockerfile, will also increase its size. Not only that, but VS Code also stores the container state on disk to reuse it across reboots, which also takes up some space.

In my own experience, Iโ€™ve seen low disk space warnings on my machine and identified old or unused Docker base images as major space consumers. Be careful while choosing the base image, and make sure you factor in storage space on your local development machine when using dev containers.

They are Linux-first

If your development is OS-agnostic or Linux-based, then dev containers should work just fine. However, if you need some other OS such as Windows or Mac, dev containers might not be as useful.

Windows does provide some images. However, youโ€™ll need to check each imageโ€™s license carefully before use, and using dev containers might not be as perfect as running an actual Windows machine.

Furthermore, MacOS does not have an official container image, so you will not be able to do Mac-based development via dev containers.

They are not VMs

In the end, containers are just processes running on the host OS. Thus, you cannot emulate different architectures with dev containers.

Also, there are some limits regarding what parts of the host system can be shared with dev containers. If you are working with something like systemd or dbus, simply using dev containers might not be enough.

Conclusion

We have seen what are dev containers, how they are beneficial for easy development setup, and how we can use them with VS Code and extensions as well as without VS Code. We also explored some of the drawbacks of dev containers and discussed when you may not want to use them.

That said, dev containers still provide enough benefits to outweigh the disadvantages in most cases. Whether you just want a single dev container to streamline your setup process or you need a more complex dev container setup, thereโ€™s likely a use case where dev containers can make your life easier.

You can check out the code we explored throughout this article in this GitHub repo. If you have further questions about using dev containers both with and without VS Code, feel free to comment below.

Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID
  2. Install LogRocket via npm or script tag. LogRocket.init() must be called client-side, not server-side

    $ npm i --save logrocket 
    
    // Code:
    
    import LogRocket from 'logrocket'; 
    LogRocket.init('app/id');
     
    // Add to your HTML:
    
    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>
     
  3. (Optional) Install plugins for deeper integrations with your stack:
    • Redux middleware
    • NgRx middleware
    • Vuex plugin
Get started now
๐Ÿ‘ Image
๐Ÿ‘ Image
๐Ÿ‘ Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension โ€” no new framework required.

๐Ÿ‘ Image
Ikeh Akinyemi
Jun 12, 2026 โ‹… 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo โ€” with email/password login, Google OAuth, session persistence, and protected routes.

๐Ÿ‘ Image
Chinwike Maduabuchi
Jun 9, 2026 โ‹… 13 min read

AI dev tool power rankings & comparison [June 2026]

Compare the top AI development tools and models of June 2026. View updated rankings, feature breakdowns, and find the best fit for you.

๐Ÿ‘ Image
Chizaram Ken
Jun 8, 2026 โ‹… 11 min read

How to check username availability at scale with Bloom filters

Learn how Bloom filters reduce database lookups for username availability checks while preserving correctness at scale.

๐Ÿ‘ Image
Rosario De Chiara
Jun 8, 2026 โ‹… 6 min read
View all posts

Would you be interested in joining LogRocket's developer community?

Join LogRocketโ€™s Content Advisory Board. Youโ€™ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now