DevContainer (devcontainer, Dev Container?) is a specification, based on containers, that describes everything a development environment needs: base image, packages, tools, environment variables, IDE extensions, etc. From this description, a development container is launched and the project is mounted inside it, providing an isolated environment that's integrated with the IDE (VS Code, Codespaces, JetBrains, etc.).
Microsoft offers "pre-made" images with the usual development tools for popular languages (Go, .NET, Node, Python, Java, etc.). These images are designed to be used directly in a devcontainer.json without having to maintain a custom Dockerfile.
You can add/combine features. Features are reusable blocks that install languages/tools (Node, Docker CLI, Git, etc.)
You can also specify extensions and settings for your IDE (explanations for VSCode)
As far as I'm concerned, I prefer to have full control over my environment and I don't use pre-made images or features:
- I create my own image, starting from a base image (for example
ubuntu:22.04), and I install all the tools I need. - Eventually I publish this image on Docker Hub to be able to reuse it easily in my projects.
- I also use Docker Compose alongside DevContainer to define additional settings (especially for building my image) and to be able to benefit from multiple services within my DevContainer workspace (like a database for example)
In this first part, I'm going to present my minimal setup for a DevContainer project:
- An Ubuntu-type base image
- A few tools (curl, git, etc.)
- Docker (to be able to launch containers from my DevContainer environment)
Project structure
In the project where I'm going to use DevContainer, I have the following structure:
.devcontainer
├── compose.yml
├── devcontainer.json
└── Dockerfile
Let's look at the content of each of these files.
Dockerfile
Let's start with the Dockerfile:
FROM --platform=$BUILDPLATFORM ubuntu:22.04
LABEL maintainer="@k33g_org"
ARG TARGETOS
ARG TARGETARCH
ARG USER_NAME=vscode
ARG DEBIAN_FRONTEND=noninteractive
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8
ENV LC_COLLATE=C
ENV LC_CTYPE=en_US.UTF-8
ARG USER_NAME=vscode: Standard username "vscode" (default value)- Portable image: Can be published on Docker Hub and reused by others
- DevContainer convention: Follows the Microsoft DevContainers standard
DEBIAN_FRONTEND=noninteractive: Avoids interactive prompts during installationLANG/LANGUAGE/LC_*variables: Locale configuration in US English (UTF-8)
# ------------------------------------
# Install Tools
# ------------------------------------
RUN <<EOF
apt-get update
apt-get install -y curl wget jq git build-essential xz-utils software-properties-common sudo sshpass unzip
apt-get clean autoclean
apt-get autoremove --yes
rm -rf /var/lib/{apt,dpkg,cache,log}/
EOF
Installing Basic Tools:
curl,wget: File downloadingjq: JSON parser for command linegit: Version controlbuild-essential: Compilers (gcc, g++, make)xz-utils: Compression/decompressionsoftware-properties-common: Repository managementsudo: Privilege elevationsshpass,unzip: Utilities
# ------------------------------------
# Install Docker
# ------------------------------------
RUN <<EOF
# Add Docker's official GPG key:
apt-get update
apt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-model-plugin
EOF
- This section installs a complete Docker stack inside the DevContainer. This section is optional if you don't need Docker in your development environment.
- Important note: The Docker CLI in the container communicates with the host's Docker Engine via the
/var/run/docker.socksocket mounted fromdevcontainer.json
# ------------------------------------
# Create a new user
# ------------------------------------
RUN adduser --disabled-password --gecos '' ${USER_NAME}
# Add new user `${USER_NAME}` to sudo group
RUN adduser ${USER_NAME} sudo
# Ensure sudo group users are not asked for a password when using
# sudo command by ammending sudoers file
RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
RUN <<EOF
groupadd docker
usermod -aG docker ${USER_NAME}
newgrp docker
EOF
# Add new user `${USER_NAME}` to docker group
RUN adduser ${USER_NAME} docker
# Set the working directory
WORKDIR /home/${USER_NAME}
# Set the user as the owner of the working directory
RUN chown -R ${USER_NAME}:${USER_NAME} /home/${USER_NAME}
# Switch to the regular user
USER ${USER_NAME}
# Avoid the message about sudo
RUN touch ~/.sudo_as_admin_successful
User creation:
--disabled-password: No password required--gecos '': No personal info requested${USER_NAME}: vscode (your local username)
Sudo configuration:
- Adding to sudo group: User can execute admin commands
- NOPASSWD: Sudo without asking for password (convenient in dev)
Docker configuration:
groupadd dockeretc.
- Allows user to execute Docker commands without
sudo
Finalization:
WORKDIR: Sets the working directorychown: Gives home ownership to the userUSER: Switches to non-root user (all following commands execute with this user)touch ~/.sudo_as_admin_successful: Avoids the sudo message on first sudo use
# ------------------------------------
# Install OhMyBash
# ------------------------------------
RUN <<EOF
bash -c "$(curl -fsSL https://raw.githubusercontent.com/ohmybash/oh-my-bash/master/tools/install.sh)"
EOF
- Installs Oh My Bash: Framework for customizing the bash shell (themes, plugins, aliases). Totally optional, it's just to "make it pretty".
- Executed as non-root user (after
USER ${USER_NAME})
Let's quickly move on to the compose.yml file.
compose.yml
services:
simple-workspace:
build:
context: .
dockerfile: Dockerfile
network_mode: "host"
volumes:
- ..:/workspaces:cached
command: sleep infinity
- Service/container name:
simple-workspacecontext: .: Build context = current directory (.devcontainer/)network_mode: "host": Container shares the host's network.volumes: Mounts the parent directory (2 levels up) in/workspaces. Your source code is accessible in the container. (cached: optimization for macOS)command: sleep infinity: Container stays active indefinitely.
Finally, the devcontainer.json file.
devcontainer.json
{
"name": "Simple Workspace",
"dockerComposeFile": "compose.yml",
"service": "simple-workspace",
"workspaceFolder": "/workspaces",
dockerComposeFile: Docker Compose file to useservice: Docker Compose service to useworkspaceFolder: Working directory in the container
"features": {},
"customizations": {
"vscode": {
"extensions": [
"pkief.material-icon-theme",
"pkief.material-product-icons"
]
}
},
"remoteEnv": {
"LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}"
},
//"forwardPorts": [3000],
remoteEnv: defines$LOCAL_WORKSPACE_FOLDERin the container, contains the workspace path on the hostforwardPorts: if uncommented, would forward port 3000 from the container to the host
"mounts": [
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind",
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.gitconfig,target=/home/vscode/.gitconfig,type=bind,consistency=cached",
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached"
],
"remoteUser": "vscode",
// Forward SSH agent from host (automatic with VSCode)
"forwardAgent": true,
// Run commands after the container is created.
"postCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder} ; sudo chmod 666 /var/run/docker.sock;"
//"postCreateCommand": "sudo chmod 666 /var/run/docker.sock;"
}
Mount 1: Docker Socket:
- Mounts the host's Docker socket in the container
- Enables Docker-in-Docker
Mount 2: Git Configuration:
- Mounts
.gitconfigfrom the host in the container${localEnv:HOME}${localEnv:USERPROFILE}: Works on Linux/macOS (HOME) and Windows (USERPROFILE)- Allows you to use your Git settings
"remoteUser": "vscode":
- Defines the user used in the container
- Uses the standard
vscodeuser (created in theDockerfile)
"forwardAgent": true:
- Enables SSH Agent Forwarding: Automatically forwards your SSH agent from the host to the container (allows you to use your SSH keys without copying them to the container, convenient for Git)
"postCreateCommand":
git config --global --add safe.directory: configures Git to consider the workspace as "safe" (avoids security warnings) ✋✋✋ Errata: you can skip this command.sudo chmod 666 /var/run/docker.sock: Gives necessary permissions for the user to use Docker without sudo (optional, depends on your Docker configuration)
To summarize
┌─────────────────────────────────────────────┐
│ devcontainer.json │
│ (VS Code Configuration + Orchestration) │
│ │
│ ┌─────────────┐ ┌──────────────┐ │
│ │ Compose │──────│ Extensions │ │
│ │ File │ │ Mounts │ │
│ └──────┬──────┘ └──────────────┘ │
└─────────┼───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ compose.yml │
│ (Docker Compose Orchestration) │
│ │
│ ┌──────────────────────────────────────┐ │
│ │ Service: simple-workspace │ │
│ │ - Build context │ │
│ │ - Network mode │ │
│ │ - Volumes │ │
│ └─────────────────┬────────────────────┘ │
└────────────────────┼────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Dockerfile │
│ (Image Building) │
│ │
│ 1. Ubuntu 22.04 │
│ 2. Install tools (git, curl, etc.) │
│ 3. Install Docker │
│ 4. Create user `vscode` │
│ 5. Add `docker` group │
│ 6. Configure `sudo` │
│ 7. Install Oh My Bash │
└─────────────────────────────────────────────┘
That's it, you can start experimenting with this basic setup. In a second part, I'll show you how I add additional development stacks (Node, Golang, etc.).
You'll find the source code for this project on my Codeberg repository: https://codeberg.org/DevContainer-Samples/01-simple
Written by
Keep reading

A mini code agent with Docker Agent + Docker Model Runner - Part 1
Build a mini local code agent with Docker Agent and Docker Model Runner: a single shell tool on the small Mellum2 model, the agent loop, and running it safely in an sbx sandbox.
Aug 1, 2026Pi Coding Agent with Devcontainer, Docker Model Runner and Agentic Compose
Set up the Pi Mono coding agent with a Devcontainer, Docker Model Runner, and Agentic Compose to work with tiny language models.
Feb 5, 2026Embedding a Web IDE and Docker Agent into `sbx`, a fully secured sandbox
Embed a web IDE and docker-agent into an sbx sandbox with Docker Model Runner for a ready-to-use secure dev environment.
Apr 18, 2026
No comments yet. Be the first to comment!