Skip to main content

Command Palette

Search for a command to run...

How I setup devcontainers for heavy development projects and why I'll never switch back

Updated
4 min read

Why I Finally Started Using Devcontainers for Heavy Development Setups

I had heard about devcontainers before, but never actually needed them fir a project. That changed when I started working on a large backend-heavy open source project.

The project itself had multiple services, background dependencies, and enough moving parts that running it directly on my machine immediately started feeling risky. Very early during setup, I noticed files being created outside my actual workspace. Some new files were appearing at root-level locations, which made me look for different solutions.

My first solution was to move development into a virtual machine, just to isolate the project from my main system. But even inside the VM, I still wanted a cleaner way to define the development environment itself, something reproducible and easy to restart.

That is where devcontainers finally became practical.

Why a Devcontainer Made More Sense Than Manual Setup

A lot of development environments work fine until you need to recreate them. That's usually where problms begin. Setting up a new development environment means handling package versions, dependencies, working with system constraints (like memory), debugging, etc.

A devcontainer solves that by turning the development environment into something explicit. Instead of remembering setup instructions, the environment itself becomes part of the project.

That means:

  • the base image is defined

  • required tools are installed automatically

  • ports are mapped intentionally (helped me a lot in my case)

  • extensions are preloaded

  • post-setup commands run by themselves

The biggest advantage is how easy development becomes after the setup.

My Main Reason: Isolation

The strongest reason I chose a devcontainer was isolation. I did not want development dependencies leaking into the machine I use every day. Even when Docker already provides containerization, a devcontainer adds another level of control because the editor, terminal, and runtime all enter the same isolated space together and one can customise which extensions they'd like to install depending on the project.

Keeping the Environment Lightweight Matters

One practical issue I faced was resource limits. The project depended on a search engine service that normally consumes noticeable memory. Running the full default setup would have made development unnecessarily heavy for my machine. So I replaced that part with a lightweight testing container.

This became an important lesson:

A devcontainer means designing a development environment that keeps only what you actually need.

That can include:

  • lighter service images

  • reduced memory usage

  • skipping nonessential services

  • development-friendly debugging tools

What I Added to the Container

I built the devcontainer on top of an existing project image and added only development-focused tools.

The devcontainer.json points to my custom docker-compose.yml and uses the main app service as entrypoint. Post-create commands run migrations, collect static files, set up my git config and handle the remaining setup.

Docker-compose overrides the defaults: standard Postgres and Redis, my light ES on port 9200, and dev-focused services with debugpy enabled. Env vars like ELASTIC_ENDPOINT=http://eslight:9200 make everything plug-and-play. Volumes keep logs and statics shared across services.

Dockerfile starts from the project's image, adds Docker CLI, debugpy for breakpoints, and a few other libraries. Runs as root for dev flexibility, exposes debug ports, and sleeps infinity so VS Code doesn't complain.

Debugging Became the Best Part

The biggest improvement after moving to a devcontainer was debugging.

Instead of entering containers manually and launching processes and monitoring logs, I exposed a debugger port directly and attached it with the editor.

For larger codebases, this saves far more time than expected.

My Workflow Now Is Extremely Simple

At this point, my workflow is basically:

  1. Open VS Code

  2. Reopen in devcontainer

  3. Wait for services to start

  4. Run debugger when needed

That is all.

No repeated environment setup.

No remembering which service needs to start first.

No local pollution.

What Devcontainers Changed for Me

Before this, I thought devcontainers were mostly useful for teams. Now I think they are equally useful for individual developers working on complex projects.

Especially when the project has many dependencies and services interact with each other, etc.

After setting up a devcontainer it becomes much easier to focus on the actual work.

Final Thought

I originally set this up because I wanted to work with an easy development environment. Adopting this reduced a lot of mental overhead. The code did not become simpler, but starting the wrok did. And sometimes that alone is enough reason to adopt devcontainers.

Now I'll try to create a devcontainer setup for every single project ;)