# althack/vscode_ros2_workspace: one devcontainer that also runs your CI

> A GitHub template that wires VSCode, a devcontainer and a GitHub Actions workflow to the same ROS 2 configuration, so the distro you develop against is the distro CI builds. The price is a hard dependency on Docker, the Dev Containers extension and the template's own container image.

**althack/vscode_ros2_workspace** — A template for using VSCode as an IDE for ROS2 development.

- Repository: https://github.com/althack/vscode_ros2_workspace
- Website: https://althack.dev/vscode_ros2_workspace/
- Stars: 1,007 · Forks: 189
- Language: Python
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/althack-vscode-ros2-workspace

## What the template actually contains

The top level of the repository is short enough to list in full: a `.devcontainer/` directory, a `.github/` directory, a `.vscode/` directory, a `src/` directory, a `.gitignore`, a `LICENSE`, a `README.md`, and three shell scripts named `setup.sh`, `build.sh` and `test.sh`.

That file list is the design in miniature. The `.devcontainer` directory describes the container image and the ROS 2 version. The `.vscode` directory holds the task definitions, the launch configurations and the style settings. The `.github` directory holds the workflow. The `src` directory holds a `ros2.repos` file listing the repositories to import. And the three scripts sit in between, so that the same commands run from a VSCode task and from a CI job.

The stated goal is narrow: set up ROS 2 with VSCode as the IDE. The mechanism that makes it more than a settings dump is that the development environment and continuous integration are described once, in the devcontainer, and the CI jobs consume that same definition. The README is explicit that the build, test and lint jobs run using the same devcontainer configuration used for development.

Licence is Apache-2.0, which carries an explicit patent grant and the notice obligations for redistribution. The LICENSE file is in the repository root, and a template that people copy into their own repositories is exactly the case where those obligations matter, so read that file rather than a summary of it.

## One line sets the ROS distro for both IDE and CI

The ROS 2 version is selected by a single feature entry in `.devcontainer/devcontainer.json`:

```jsonc
"ghcr.io/althack/devcontainers/ros2:0": {
  "distro": "lyrical",
  "package": "desktop"
}
```

The README explains the payoff in plain terms. This is also the ROS version used in CI, so you only need to change it in one place. The same block appears in the section about changing the ROS distro, with the instruction to change `distro` to the version you want and rebuild the container. The `package` value is set to `desktop`, meaning the container installs the desktop-flavoured ROS 2 package rather than a minimal one.

The image itself comes from the author's own registry, `ghcr.io/althack/devcontainers/ros2:0`, and it is used together with the Microsoft Ubuntu devcontainer image and the ROS2 devcontainer feature. That is a meaningful dependency: your environment is defined by a container image somebody else publishes and bumps. The tag `:0` is a major version, so a rebuild can pick up changes you did not make.

For a team, this is the core bargain. You trade control over the base image for the guarantee that a laptop and a CI runner install the same ROS 2 build. If your organisation already pins a ROS 2 image by digest or maintains its own base image, that bargain stops making sense.

## Tasks, launch configurations and the three shell scripts

The interactive workflow is defined by two files the README points at: `.vscode/tasks.json`, described as containing many pre-defined tasks, and `.vscode/launch.json`, which holds the debugging configuration.

Debugging coverage is stated plainly: the template sets up debugging for python files, for gdb with cpp programs, and for ROS launch files. The README also links to a separate article by the author on how they develop using VSCode tasks, described as an idea for how tasks are used in development.

The three scripts at the repository root are the part that matters most for CI, because the README says they are used both within tasks and within CI:

* `setup.sh` holds setup commands for your code, and by default imports the workspace and installs dependencies.
* `build.sh` holds build commands, and by default uses `--merge-install` and `--symlink-install`.
* `test.sh` holds test commands.

The instruction is to adjust the scripts to your liking, which tells you plainly that the defaults are placeholders. That is a fair deal for a template, but it also means the CI is only as correct as the scripts you replace, and the replacement happens after the first CI run has already told you something.

Style is handled in the same template: ROS2-approved formatters are included in the IDE, with c++ uncrustify configured from `ament_uncrustify`, and python autopep8 with VSCode settings consistent with the ROS 2 code style guide.

## The seven linters in the CI matrix and how to remove one

The CI setup is described as basic, and the linter list is a matrix in `.github/workflows/ros.yaml`. The README's instruction for removing a linter is unusually concrete: just delete its name from the matrix.

```yaml
matrix:
  linter:
    - cppcheck
    - cpplint
    - uncrustify
    - lint_cmake
    - xmllint
    - flake8
    - pep257
```

Seven linters cover C++, CMake, XML and Python. `cppcheck` and `cpplint` are static analysis and style checks for C++, `uncrustify` matches the formatter configured in the IDE, `lint_cmake` and `xmllint` handle build and markup files, and `flake8` and `pep257` handle Python linting and docstring conventions.

Two things follow. First, the linters are selected by matrix dimension, which means each one runs as a separate job, and removing one line is the only knob offered. Second, the fact that `uncrustify` appears in the same list as the IDE's configured C++ formatter means the CI job and the editor agree on style, which is the usual reason teams want a linter matrix at all. A team that has already standardized on a different linter set will find this list something to prune rather than something to adopt.

Note the `flake8` and `pep257` entries alongside the IDE's `autopep8`: the formatter is autopep8, and the linters that check Python are flake8 and pep257. These are different tools with overlapping goals, and the template enables all three.

## From Use this template to a working container

The prerequisites are Docker and VSCode with the Dev Containers extension. On Windows the recommendation is to use WSL2 and open the repository from the WSL distro before reopening it in the container.

The getting-started path is entirely in the GitHub interface. Click Use this template, then on the next dialog name the repository and choose its visibility. GitHub creates a new repository with the contents of the template in your account, grabbing the latest changes as the initial commit. Clone it as normal, then open it in VSCode with File, then Open Folder.

On the first open you should see a popup asking whether you would like to reopen it in a container, and the answer is yes. If the popup does not appear, open the command palette and run:

```text
Dev Containers: Reopen in Container
```

VSCode then sets up the container from `.devcontainer/devcontainer.json`, installs ROS2 and the configured development tools, and installs the recommended VSCode extensions. When that finishes, open a terminal inside VSCode and you are ready to go.

The follow-up steps are all task-driven, and they are the sequence a new user should follow first. Specify the repositories to include in `src/ros2.repos`, or delete that file and develop directly in the workspace. If you keep the file, import its contents with Terminal, then Run Task, then import from workspace file. Install dependencies with Terminal, then Run Task, then install dependencies. Then adjust the three scripts, and develop.

## GUI, CUDA and the WSL2 display forwarding problem

Getting a GUI application to display from inside the container is the part of this template with the most conditional instructions, and the conditional is a good sign that it has been fought over.

On Windows and WSL2, the README says current versions of VSCode Dev Containers and WSLg handle display forwarding for you, so you should not need to configure `DISPLAY`, Wayland, PulseAudio or `/mnt/wslg` mounts manually.

On native Linux with X11, you uncomment the X11 forwarding feature in `.devcontainer/devcontainer.json`:

```jsonc
"ghcr.io/althack/devcontainers/linux-x11-forwarding:0": {},
```

And if some GUI applications need to share the host IPC namespace, uncomment the `--ipc=host` flag in `runArgs`.

CUDA is optional. Uncommenting the NVIDIA CUDA feature enables the toolkit install, and `runArgs` needs the GPU passthrough flags:

```jsonc
"ghcr.io/devcontainers/features/nvidia-cuda:3": {
  "installToolkit": true
}
```

```jsonc
"--gpus",
"all"
```

The host needs working NVIDIA drivers and the NVIDIA Container Toolkit. The author is candid about the limits of this area, writing that they do not test all of the possible NVIDIA/CUDA host configurations, so your mileage may vary. That is the single most likely place for this template to let you down, and it is the place where the template has the least evidence behind it.

## Where this template stops and the alternatives begin

The clearest alternative is to not use a template and instead write the devcontainer yourself, or to base the environment on a ROS 2 image published by the ROS project rather than on the author's `ghcr.io/althack/devcontainers/ros2:0` image. The difference in approach is ownership. This template hands you a working combination of an Ubuntu base, the ROS2 devcontainer feature, tasks, launch configs, linters and CI, and it asks for nothing in return beyond editing three shell scripts. Writing it yourself gives you a base image you control, but you also inherit the work of wiring CI to the devcontainer, which is exactly the part the template gets right first time.

A second alternative is a plain GitHub Actions workflow with a ROS 2 setup step and no container at all. That is simpler for CI but leaves the local environment and the runner as two separate configurations, which is the problem this template exists to solve.

The template also has honest limits. There is no GitHub release for the repository, so there is no version to pin: you take whatever the default branch holds. The last push was on 2026-08-18 and the repository is not archived, so it is being worked on, but the distribution channel is the template button rather than a tagged artifact, and there is no changelog to read.

There is also a friction point that has nothing to do with ROS 2. Because the environment lives in a container, your editor state, your build cache and your ROS 2 installation all live inside it, and the local machine needs Docker running for the whole development day. Editors that integrate with the ROS 2 tooling outside a container will not see any of it.

## Conclusion

This template suits ROS 2 developers who want a laptop, a teammate and a CI runner to agree on the same toolchain without writing a Dockerfile from scratch, and who are willing to keep Docker running locally. It does not suit teams that build on bare metal, that run their own CI runner image, or that want a ROS 2 version the template's `distro` value does not name. Before adopting it, confirm that the ROS 2 feature in `.devcontainer/devcontainer.json` names a distribution you actually want, replace the placeholder `setup.sh`, `build.sh` and `test.sh` scripts with your own, and check the `linter` matrix in `.github/workflows/ros.yaml`, since the template ships with seven linters enabled by default and removing one is a matter of deleting its name from that matrix.

## FAQ

### How do I change the ROS 2 version in althack/vscode_ros2_workspace?

Change the `distro` value in the ROS2 feature block in `.devcontainer/devcontainer.json` and rebuild the container. CI uses the same devcontainer configuration, so the change applies to both without any other edit.

### Do I need Docker to use the vscode_ros2_workspace template?

Yes. The prerequisites are Docker and VSCode with the Dev Containers extension, and the environment is defined by a devcontainer that VSCode builds from `.devcontainer/devcontainer.json`.

### How do I remove a linter from the CI checks?

Delete its name from the `linter` matrix in `.github/workflows/ros.yaml`. The matrix lists cppcheck, cpplint, uncrustify, lint_cmake, xmllint, flake8 and pep257 by default.

### What are setup.sh, build.sh and test.sh used for?

They hold the setup, build and test commands for your code, and the README says they are used both within VSCode tasks and within CI. The default build script uses `--merge-install` and `--symlink-install`, and the default setup script imports the workspace and installs dependencies.

### Can I run GUI applications from the ROS 2 devcontainer?

On Windows and WSL2, VSCode Dev Containers and WSLg handle display forwarding for you. On native Linux with X11 you uncomment the X11 forwarding feature in `.devcontainer/devcontainer.json`, and you may also need `--ipc=host` in `runArgs`.

## Sources

- [althack/vscode_ros2_workspace on GitHub](https://github.com/althack/vscode_ros2_workspace)
- [Issues](https://github.com/althack/vscode_ros2_workspace/issues)
- [License: Apache-2.0](https://github.com/althack/vscode_ros2_workspace/blob/main/LICENSE)
- [Project website](https://althack.dev/vscode_ros2_workspace/)
- [README](https://github.com/althack/vscode_ros2_workspace/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/althack-vscode-ros2-workspace
