Skip to content

300bps/zephyr-containerized-dev-env

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

Zephyr Application Development Environment

D. Smith - 9/23/2025

Purpose:

The purpose of this environment is to use isolated Docker containers to simplify rapid creation and setup of isolated Zephyr firmware development projects using Docker and VS Code. Container isolation ensures that multiple development projects can co-exist on the same host machine without cross-poluting the development setup for any one of them.

VS Code automation and hooks are used to simplify the creation of a new development environment container by simply downloading this template project into the desired working folder on the host, ensuring that VS Code and the Dev Container VS Code extension is installed, navigating to the app1 (or app2) subfolder, and launching VS Code from this location. The Dev Container automation will use the custom Dockerfile to create a new Docker image, installing and updating it with the specified version of Zephyr, the Arm development backend (arm-none-eabi toolchain), and other required tools.

Note: The first time a new image is built will take a significant amount of time as the entire Zephyr SDK and code base will be downloaded and installed in the container.

After the new image is built, the automation will create a new development container from it and install the VS Code development extensions into the container so that it will be ready to use. From that point on, it can be reused with minimal delay each time that same development environment is opened.


Initial Host Setup

This section describes the process of setting up the host development environment for the very first time. This should only be done once.

Requirements and Recommendations:

  • Linux Host (Recommend Debian-based Linux VM like Ubuntu or Mint) with a minimum of 60GB free disk space.
  • VS Code
  • Docker Engine (NOT Docker Desktop)
    • Install using APT: sudo apt install docker.io --yes
  • JLink Software
  • JLink Plus (or better) Hardware Debug Adapter

Host Configuration Instructions:

  1. Install a Debian Linux variant to act as the host OS (hereafter referred to as the host). Ubuntu or Linux Mint are good options. This may be a virtual machine or bare metal installation. The reference configuration used during development of the template consists of Linux Mint installed as a virtual machine on VirtualBox, which was running on Windows 11.
  2. Update and upgrade the host. Use the built-in software manager or APT from the command line (sudo apt update --yes && sudo apt upgrade --yes).
  3. Install Docker Engine. Use the software manager or APT (sudo apt install docker.io --yes).
  4. Add the current user to the 'docker' group. From the command line, use the following command (sudo adduser yourusername docker).
  5. Download JLink Software.. Manually download the correct version of the "JLink Software and Documentation Pack" for the host (Linux 64-bit DEB installer).
  6. Install downloaded Jlink Software. Run GUI OS package installer on downloaded .deb file, or use command line from the directory containing the downloaded file (sudo apt install ./downloaded_package_name.deb --yes).
  7. Install VS Code. Use software manager or APT.
  8. Install VS Code Microsoft Extension Dev Containers. Open VS Code and select the extension manager feature. Search for the container in the container store and install.

Zephyr Template Environment Initial Installation:

  1. Get the template project from the github repo and place it in the desired development root folder location.
  2. In the terminal, navigate to the app1 subfolder. This will be the initial project development folder.
  3. Important: Launch VS Code from this location (../app1). This can be done as follows: code .
  4. In the VS Code user interface, select the option to "Reopen in Container". This option will either appear in a pop-up, or can be selected from the blue "open a remote" >< icon in the lower left.
  5. Select the "Connecting to Dev Container (show log)" pop-up to be able to watch and track the image build progress.
  6. The very first time this is done, VS Code with build the Docker image for future development containers. This process will likely take > 45 minutes. Allow it to complete. Note: the Docker image can also be built manually (outside of VS Code) and may complete quicker when built this way. To build manually, from the .devcontainer folder: sudo docker build .
  7. Ignore the "There are task errors ..." popup. This is a one-time false positive.
  8. After the container build HAS COMPLETED. Close VS Code.
  9. RECOMMENDATION: Once initial image build has completed successfully, manually tag the resulting Docker image to prevent the need to repeat long image builds in the future.
    1. To see Docker local built images, use sudo docker image ls
    2. Tag the resulting image as zephyr_env:latest with: sudo docker image tag <built image ID> zephyr_env:latest
  10. Reopen VS Code from the app1 folder. Again, select "Reopen in Container".
  11. The environment is now ready for use. Place application development code in the app1 folder and begin.


Development of Multiple Firmware Applications:

This step can be taken each time a new development environment sandbox for an additional Zephyr project based on the same template environment is to be created.

For a second app (app2):

  1. For the second app, switch to the ./projects/app2 folder.
  2. Launch VS Code from this location. It will build a new container using the local tagged zephyr_env:latest image. Note: this requires that you followed the recommendation to manually tag the Docker image as zephyr_env:latest following build completion.
  3. Follow "Zephyr Template Environment Initial Installation" steps 4 through 6. This time, the build will complete in < 5 minutes due to the use of the tagged image.
  4. Follow "Zephyr Template Environment Initial Installation" steps 7 and 8.
  5. Skip step 9 as this has already been done.
  6. Follow steps 10 through 11.

For three or more apps:

  1. Create a new folder under ./projects for the new app using the form of app# using the number of the app in place of the # sign.
  2. Copy the following 2 folders and their contents to the newly created project folder: ./projects/app2/.vscode and ./projects/app2/.devcontainer
  3. Switch to the newly created app folder and follow "For a second app (app2)" steps 2 through 6.


Configuration and Customization:

The template has been designed to simplify the act of converting from one development target to another (i.e. boards or micros). The default configuration is for development for the Raspberry Pi Pico (RP2040), but can be altered to target other platforms as described below. Likewise, it can be easily modified to target development using different released version of Zephyr as also described below. See the Template Layout section for a detailed description of the file system layout the files therein contained.

Configuration Options:

  • Selecting the Zephyr "board" hardware target:

    1. Open the ./projects/app#/.vscode/settings.json file.
    2. Set the "project.BOARD" value to the desired legal value for the target hardware. Available legal values can be listed using the west boards command.
    3. Set the "project.JLINK_DEBUG_DEVICE" to the JLink target identifier for the target hardware. Valid values can be found at JLink Targets.
  • Selecting the project's top-level devicetree overlay file:

    1. Open the ./projects/app#/.vscode/settings.json file.
    2. Set the "project.DTCOVERLAY_FILE" to the name of the DT overlay file located in the project's ./boards folder.
  • Selecting the version of Zephyr to include in the Docker build image:

    1. Open the ./projects/app#/.devcontainer/assets/workdir/manifest-repo/west.yml file.
    2. Change the value of the "revision" key under the zephyr project heading to the desired value (.e.g. revision: v2.5.0).
    3. Delete the tagged zephyr_env:latest image from the local Docker cache: sudo docker image rm zephyr_env:latest
    4. Select Docker build option 1 and comment out Docker build option 2 in the associated devcontainer.json file. This will instruct Dev Containers to build an image from scratch using the Dockerfile.
    5. Either allow VS Code to build the new image as discussed elsewhere, or manually build it from the command line using: sudo docker build .
    6. RECOMMENDATION: Manually tag the resulting Docker image as discussed in step 9 of "Zephyr Template Environment Initial Installation".
    7. RECOMMENDATION: Follow the steps described elsewhere in "Configuration Options" to select the local build image for future use.
  • Adding additional modules or external project resources:

    1. Open the ./projects/app#/.devcontainer/assets/workdir/manifest-repo/west.yml file.
    2. Edit this file to include the desired additional items.
    3. Rebuild the zephyr_env:latest Docker image as described in steps 3 through 7 of the "Configuration Options" item that describes selecting the version of Zephyr to include in the Docker build image.
  • Source-level debug: choosing between use of container's internal JLinkGDBServer versus host's "external" JLinkGDBServer:

    1. Open ./projects/app#/.vscode/launch.json file.
    2. To use container's internal JLinkGDBServer, uncomment section 1 and comment out section 2.
    3. To use host's "external" JLinkGDBServer, comment out section 1 and uncomment section 2.
  • After first build (and tag) of zephyr_env Docker image, select the local build image for future use:

    The benefit of doing this AFTER the initial image build completes is that all future container creation will happen in around ~1 minute using the previously constructed image. Note: this requires that you followed the recommendation to manually tag the Docker image as zephyr_env:latest following build completion.

    1. Open ./projects/app#/.devcontainer/devcontainer.json file.
    2. Comment out Docker build option 1 and uncomment Docker build option 2.


Details: Template Design Decisions and Implications:

This section describes why certain descisions were made in the design of this template and the implications of those decisions. Additionally, some potential modifications are also discussed should they become necessary at some future time.

  1. Goals:

    1. Use Docker to create clean, isolated build environments that include all the necessary toolchains and SDKs to enable development of Zephyr applications.
    2. Attempt to make the template easily retargetable to any of the hardware targets supported by Zephyr.
    3. Include all the drivers and configuration required to enable source-level debugging using the industry-standard JLink debug adapter.
    4. Automate the process of Docker builds of the development images and containers.
    5. Make use of the VS Code Dev Containers extension to install all of the necessary extensions in the container and allow build and debug to happen in the isolated container. This keeps multiple development environments from cross-polluting one another with tools, configuration, or code.
    6. Structure the template so that multiple application projects could be developed in isolated containers that share a base Docker image (zephyr_env). This may be necessary for projects that require a main application project and a bootloader project that all use the same base Docker image, but that still have the benefit of isolation.
    7. Support extension to multiple Docker images built for development with different Zephyr versions all coexisting on the same host system. This will require some minor modifications to the recommendation in this document - namely using separate tag names for the various versions. One recommended approach would be zephyr_env:v4.0.5 with the v4.0.5 being replaced with the version of zephyr contained. Then, the devcontainer.json file would be modified so that the Docker option 2 image would point to the specific tag of the version being used in that project.
  2. Development Environment Architectural Diagrams:

    • Internal: Development Environment using GDB Server in Container:

      Internal

    • External: Development Environment using GDB Server on Host:

      External

  3. Structural Considerations:

    1. One considered structure that was ultimately rejected was to eliminate the ./projects folder and have a single app folder. This simplified the structure of the project tree for the single application development case, but didn't allow for multiple projects to reuse the same Docker image. Ultimately, the knowledge that our specific development opportunities will likely require at least 2 application projects (the app and the bootloader) resulted in going with the more complicated, but more flexible structure that includes the ./projects folder.
    2. External modules / projects not included with Zephyr could be included / accomodated in two ways.
      1. Modify the west.yml manifest file in the ./manifest-repo folder to include it directly in the Docker image. Then trigger a full rebuild of the image. This will make the image a customized, non-vanilla Zephyr image, so it may make sense to do it on a per-project basis rather than as a global update to the template.
      2. A separate approach is to place the modules / projects in their own folder at a level equal or higher than the ./projects folder. Then modifiy the devcontainers.json file to create a shared "bind mount" of the folder into the container under the West topdir folder. In this development template, topdir is the /workdir folder in the container.
  4. Updating the Template:

    1. Additional VS Code extensions can be added to the devcontainer.json file in the "customizations" section to have them automatically installed by default in the development container.
    2. As newer versions of the JLink tools are released in the future, it may be desirable to update the template to use them. To do so, replace the JLink_Linux_V870_x86_64.deb file in the various ./assets folders with the newer released package. Make sure the new package name still begins with JLink_ as this is how the Dockerfile will find and include it in the image build. Then force a rebuild of the Docker image as described elsewhere.
    3. As newer version of the Arm compiler toolchain are released (arm-none-eabi) in the future, it may be desirable to update the template to use them. To do this, edit the ./.devcontainer/Dockerfile and update the values assigned to "C_ARM_TOOLCHAIN_TARBALL_FILENAME" and "C_ARM_TOOLCHAIN_TARBALL_URL". Then force a rebuild of the Docker image as described elsewhere.
  5. Miscellaneous:

    1. Versions of components with which the template was developed and tested:

      1. VirtualBox: version 7.1.8
      2. Linux Host OS: Linux Mint v22.1 (Xia)
      3. Docker Engine from docker.io package: Docker version 27.5.1, build 27.5.1-0ubuntu3~24.04.2
      4. VS Code: version 1.102.1
      5. VS Code Extensions:
        1. Dev Containers: Microsoft version 0.427.0
        2. C/C++: Microsoft version 1.27.7
        3. CMake Tools: Microsoft version 1.21.36
        4. Python: Microsoft version 2025.14.0
        5. Pylance: Microsoft version 2025.8.2
        6. Python Debugger: Microsoft version 2025.10.0
        7. Python Environments: Microsoft version 1.8.0
        8. Cortex-Debug: marus25 version 1.12.1
        9. Embedded Tools: Microsoft version 0.8.0
        10. MemoryView: mcu-debug version 0.0.27
        11. Peripheral Viewer: mcu-debug version 1.6.0
        12. RTOS Views: mcu-debug version 0.0.11
        13. debug-tracker-vscode: mcu-debug version 0.0.15
        14. nRF DeviceTree: Nordic Semiconductor version 2025.8.140
        15. nRF Kconfig: Nordic Semiconductor version 2025.9.123
      6. Jlink Software: Segger version 8.70
      7. Arm GNU Toolchain: Arm Holdings version 14.3.1
    2. Docker:

      1. VS Code with Dev Containers simplifies the use and management of Docker images and containers for development, however some direct command line manipulation may be necessary from time to time.
      2. The following are some examples that may require direct command line user of Docker:
        1. Manually tagging a Docker build image.
        2. Deleting a failed Docker build artifact or container.
        3. Deleting containers and / or images to free up disk space.
        4. Viewing the amount of space consumed by Docker artifacts.
        5. Manually triggering Docker builds (faster than build via Dev Containers).
        6. Manually entering a Docker container for customization or debugging.


Details: Template Layout:

The file structure of the template is deliberate and locates the configuration files expected by VS Code, Dev Containers, and Docker in the correct relative locations. The overall structure is as shown below with the file tree anchored in the user-selectable development folder (which can be given any valid name) and located anywhere in the local file system.

[Top-level Development Folder]
└── projects
    ├── app1
    │   ├── .devcontainer
    │   │   ├── assets
    │   │   │   ├── JLink_Linux_V870_x86_64.deb
    │   │   │   └── workdir
    │   │   │       ├── manifest-repo
    │   │   │       │   └── west.yml
    │   │   │       └── projects
    │   │   │           ├── app1
    │   │   │           └── app2
    │   │   ├── devcontainer.json
    │   │   └── Dockerfile
    │   └── .vscode
    │       ├── launch.json
    │       ├── settings.json
    │       └── tasks.json
    └── app2
        ├── .devcontainer
        │   ├── assets
        │   │   ├── JLink_Linux_V870_x86_64.deb
        │   │   └── workdir
        │   │       ├── manifest-repo
        │   │       │   └── west.yml
        │   │       └── projects
        │   │           ├── app1
        │   │           └── app2
        │   ├── devcontainer.json
        │   └── Dockerfile
        └── .vscode
            ├── launch.json
            ├── settings.json
            └── tasks.json

Important Locations:

  • Folder projects:

    This folder lives at the virtual root of the Zephyr West workspace, and is at the same level as the zephyr folder in a typical Zephyr install. Using this folder to contain the application folders (app1, app2, etc) allows for multiple projects to be developed using the same West workspace (e.g. a bootloader and application each in separate app folders).

  • Folder app1 (or app2):

    This/these folders contain the firmware under development. Additional app folders can be added as desired so long as the .vscode and .devcontainer folders are copied from app2 to the additional app folders.

  • Folder ./projects/app#/.devcontainer:

    Hidden folder containing the items necessary to build the Docker image.

  • File ./projects/app#/.devcontainer/devcontainer.json:

    The Dev Container configuration file specifies how the Docker image is built, which volumes to share with the container, and which VS Code extensions to add to the container following the build.

  • File ./projects/app#/.devcontainer/Dockerfile:

    The file that describes how Docker builds the desired image.

  • Folder ./projects/app#/.devcontainer/assets:

    Folder contains assets used by the Dockerfile during the image build process.

  • File ./projects/app#/.devcontainer/assets/JLink_Linux_V870_x86_64.deb:

    The Segger JLink Software package that will be installed in the Docker image during the build process.

  • File ./projects/app#/.devcontainer/assets/workdir/manifest-repo/west.yml:

    The West manifest file that describes the T3 layout of the West workspace, specifies the version of Zephyr to use, and specifies any additional dependencies/modules to install.

  • Folder ./projects/app#/.vscode:

    Hidden folder containing the files that determine how VS Code can build, FLASH, and debug projects.

  • File ./projects/app#/.vscode/settings.json:

    File specifies variables that can be used in the other json files in the .vscode folder. Variables in the file allow easy selection of development target hardware for build and debug and specification of the top-level project devicetree overlay.

  • File ./projects/app#/.vscode/tasks.json:

    File defines the "tasks" that VS Code uses to build the project and FLASH the hardware.

  • File ./projects/app#/.vscode/launch.json:

    File enables source-level debugging via GDB and GDBServer.

About

A containerized Zephyr development and build environment using Docker, VS Code, and DevContainers. Retargetable to the various hardware platforms supported by Zephyr (default: Raspberry Pi Pico).

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors