Sitemap
Ruvinda Dhambarage

Ruvinda Dhambarage

Embedded Systems & Linux Techie

A Yocto power user’s guide to tooling: Part II

All in on VS Code Dev Containers

5 min readAug 23, 2025

--

This blog builds on top of Part I linked below:

Press enter or click to view image in full size

Why a part II ?

Two things happened since I wrote part I,

  1. I finally managed to get QEMU running inside Docker (i.e. QEMU inside Docker, inside WSL, on Windows). It turned out to be pretty trivial to setup. I just had read the code comments in the Yocto runqemu script🤦🏻‍♂️. This invalidated my premise in part I of needing to re-create your local folder structure inside the Docker container.
  2. Seeing first-hand how people struggle with the Docker container setup I prescribed in part I. Supposedly it’s not very beginner friendly.

So, that lead me to give VS Code’s Dev Containers a shot in an attempt to make the setup n00b friendly. This feature effectively lets you run VS Code inside of a Docker container, much like how remote debugging with gdbserver works. Basically, VS Code UI is running on your host machine but it’s executing everything behind the scenes in a “remote” instance. I had previously dismissed it primarily because I couldn’t get QEMU working and also because it had left a bad taste in my mouth when I had used it in the past. But it turned out that my prejudice for Dev Containers was unjust; it is a thing of beauty when setup right!

The VS Code setup

A complete working repo is at:

I’ll go over the key files and concepts below:

1. Move settings to the Dev Container config file

In part I, I showed how to write a VS Code extensions configuration file to install recommended extensions and how to properly configure the Bitbake plugin settings via a workspace configuration file.

Now with Dev Containers, you can move all of that to a devcontainer.json file. The only reason to keep a workspace file is if you want to open/manage all of your Yocto layers through VS Code.

With this updated setup, the extensions are installed inside the Docker container. This is a pretty neat feature that helps minimize the extension bloat on VS Code that would otherwise slow it down when you use it for non-Yocto work as a general purpose editor.

Pro tip: you can set VS Code as the default editor for git.

2. Using Docker Compose

For bootstrapping a Yocto dev environment, I’d recommend using Docker Compose as oppose the typical Dev Container image configs approach. Docker Compose lets you use variables like ${HOME} for the mount paths, making the configs portable. e.g. https://github.com/ruvi-d/simplest-yocto-setup/blob/main-without-ai/docker-compose.yml

services:
crops-yocto:
image: crops/yocto:ubuntu-22.04-base
command: sleep infinity
volumes:
# Mount working dir
- .:/workspaces/simplest-yocto-setup
# Mount shared caches dir
- ${HOME}/yocto-caches:/workspaces/simplest-yocto-setup/caches
# Mount SSH credentials for git access
- ${HOME}/.ssh:/home/yoctouser/.ssh:ro
# Mount shell history files
- ${HOME}/.bash_history:/home/yoctouser/.bash_history

The above Compose config replaces the following Docker command I shared in part I

 docker run --volume=$(pwd):$(pwd) -u $(id -u):$(id -g) \
-v /etc/passwd:/etc/passwd:ro -v /etc/sudoers:/etc/sudoers \
-v ${HOME}/.ssh:${HOME}/.ssh \
-v /etc/ssh/ssh_known_hosts:/etc/ssh/ssh_known_hosts \
-v ${HOME}/.gitconfig:${HOME}/.gitconfig \
-v ${HOME}/.bash_history:${HOME}/.bash_history \
--workdir=$(pwd) --interactive --tty --name my-yocto-builder \
crops/yocto:ubuntu-22.04-base

Dev Containers takes care of passing through .gitconfig, so you don’t need to mount it. The VS Code documentation has more elaborate instructions to pass the Bash history and SSH git credentials, but I’d recommend the way I have done here. This way it “just works” for any new developer setting up your repo.

3. Launching a VS Code workspace inside a Dev Container

I concede that the biggest pain with Dev Containers is that it requires a two step process to launch.

Get Ruvinda Dhambarage’s stories in your inbox

Join Medium for free to get updates from this writer.

Step 1: Open VS Code in the workspace like you would normally do

Step 2: Hit F1 or ctrl+shift+p to bring up the command palette, type and select: “Dev Containers: Open Workspace in Container…”

Press enter or click to view image in full size
The two step process of opening a project inside a Dev Container

4. Dev Container features

Dev Container features is a cool feature that let’s you add stuff like your favorite dev tools to a Docker Image. Without this, developing inside a Docker container can be pretty limiting as you wouldn’t have access to your preferred tools. Dev Container features lets you layer stuff on top of a Docker image without you having to maintain a customer image yourself.

In the simplest-yocto-setup demo project I linked above, I have added my favorite shell ZSH, along with the auto-suggestion plugin to the CROPS build image.

The list of available features can be found at: https://containers.dev/features. I’ve picked https://github.com/nils-geistmann/devcontainers-features/blob/main/src/zsh/README.md and added it via:

 "features": {
"ghcr.io/nils-geistmann/devcontainers-features/zsh:0": {
"plugins" : "git zsh-autosuggestions"
}
},

The above config enables the zsh-autosuggestions plugin but doesn’t actually download it. To do that I’ve used a postCreateCommand as follows:

"postCreateCommand": "git clone https://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions",

To make the auto-suggestions actually useful, I have to also mounted my host machine’s ZSH history via the following Docker Compose config excerpt:

services:
crops-yocto:
volumes:
# Mount ZSH shell history files
- ${HOME}/.zsh_history:/home/yoctouser/.zsh_history

Then to wrap everything up, I’ve made ZSH my default terminal profile in the Dev Container via the following config in devcontainer.json:

"terminal.integrated.defaultProfile.linux": "zsh",

Voila! You now got ZSH on the CROPS build image, with auto suggestions!

Press enter or click to view image in full size
ZSH!!!

5. Building and running qemu

Bonus tip: if you typically build multiple machine configures, you can create multiple build directories to isolate these builds and have them be nicely managed via the Bitbake plugin. All you need to do is define “buildConfigurations” as shown below.

"bitbake.buildConfigurations": [
{
"name": "dogbonedark",
"pathToBuildFolder": "${workspaceFolder}/build"
},
{
"name": "stompduck",
"pathToBuildFolder": "${workspaceFolder}/build-st"
},
{
"name": "qemuarm",
"pathToBuildFolder": "${workspaceFolder}/build-qemuarm"
}
],

Use KAS to create these build folders with the appropriate configurations. e.g.

AS_BUILD_DIR=build-qemuarm KAS_MACHINE=qemuarm kas checkout

Now, let me show how to switch to the qemuarm build configuration and launch QEMU via the command

build-qemuarm> bitbake kiss-image
build-qemuarm> runqemu qemuarm slirp nographic
Press enter or click to view image in full size
QEMU in Docker, in WSL, in Windows.. 🐢🐢🐢

`slirp` is the magic arg that lets you run QEMU inside the CROPS container using usermode networking.

That’s all folks. I’m pretty happy with this updated more n00b friendly setup. Hope you find it useful!

--

--