This is the setup I recommend to students in the Master’s degree in Robotics and Artificial Intelligence at the University of León.

Different courses can require different ROS distributions, Ubuntu releases, or dependency versions. Docker lets you keep them side by side without installing a specific ROS version on the host system or trying to make one global Python and pip environment work for every project. Each container has its own ROS installation, system dependencies, and Python packages so the host stays clean.

The result is a ROS VNC desktop that opens in the browser, a workspace that stays on the host machine, and VS Code attached directly to the container. It makes it easy to iterate on a project, switch to another course with different requirements, and return without having to repair the local environment first.

Install Docker

Install Docker Engine for Ubuntu by following the current official instructions. The commands and supported Ubuntu versions change often enough that copying them into this post would make the guide age badly.

Verify that Docker can run a container:

docker run hello-world

If Docker asks for sudo, follow Docker’s post-install instructions. Adding a user to the docker group gives that user privileged access to the Docker daemon, so it is a convenience for a personal development machine, not a harmless permission change. Log out and back in after adding the group.

Docker Desktop is optional. It can be convenient if you prefer a graphical view of images and containers, but the workflow in this guide only needs the Docker CLI.

Choose and start a ROS environment

For new work, ROS 2 Lyrical is the default below. Jazzy is a mature LTS option, while Humble is useful when an existing project still depends on it. ROS 1 Noetic remains available only for courses and projects that require it, because it is no longer supported upstream.

Latest ROS 2 LTS release. This image tag is pinned for reproducibility.

Workspace on your machine: ~/ros2_lyrical_ws

Open in VS Code: /home/ubuntu/ros2_lyrical_ws

Container name ros2_lyrical_vnc

Start the container

mkdir -p ~/ros2_lyrical_ws

docker run -d \
  --name ros2_lyrical_vnc \
  -p 127.0.0.1:6080:80 \
  --mount type=bind,source=$HOME/ros2_lyrical_ws,target=/home/ubuntu/ros2_lyrical_ws \
  -e RESOLUTION=1600x900 \
  --shm-size=512m \
  tiryoh/ros2-desktop-vnc:lyrical-20260621T0550

Then open http://localhost:6080

The bind mount is the important part of the command. The selector shows the workspace for the distribution you chose. It is stored on the host, not only in the container filesystem, so you can stop or recreate the container without losing the project files. Keeping one workspace per distribution also makes it safe to have several ROS environments at the same time.

The port binding starts the browser desktop at http://localhost:6080. Binding it to 127.0.0.1 keeps it accessible only from the same machine. If the port is already in use, change the first 6080, for example to 6081.

The images used here come from tiryoh’s ROS desktop VNC images. They are convenient for coursework because ROS and a lightweight desktop are already set up. For a long-lived project, pin an image digest or maintain a small Dockerfile yourself so the environment is reproducible.

To start the same container again later, use the name shown by the selector. With the default option:

docker start ros2_lyrical_vnc

For example, Jazzy uses ros2_jazzy_vnc and Noetic uses ros1_noetic_vnc. To see all containers, including stopped ones:

docker ps -a

Connect with VS Code

Install the official Dev Containers extension in VS Code. Open the Command Palette with Ctrl + Shift + P, run Dev Containers: Attach to Running Container…, and select the container.

Then open the path shown as Open in VS Code in the selector. It is the mounted version of the workspace inside the container.

Dev Containers starts a VS Code server inside the running container. The editor window stays on the host, but the terminal, language servers, debuggers, and most workspace extensions run in the container. In practice, python, pip, colcon, and ROS commands in that VS Code window use the selected ROS environment rather than the host system. VS Code extensions that need access to the workspace are installed in the container as well; VS Code will offer that option when needed.

When you attach again, VS Code remembers the selected folder and container-specific settings. Its attached container configuration is also a good place to make that setup explicit if you use it regularly.

Stop or remove it

Closing VS Code only disconnects the editor. Stop the desktop when you no longer need it:

docker stop ros2_lyrical_vnc

Removing a container is fine once the workspace is mounted on the host:

docker rm ros2_lyrical_vnc

That removes the desktop environment and any changes made only inside the container. Your source code remains in the workspace shown by the selector.