---
title: Building ROS 2 snaps with Colcon
description: The snapcraft CLI has supported building ROS1 snaps for a while via the
  catkin plugin. We supported the ROS2 betas via the ament plugin, but that was before
  Open Robotics had a ROS2 package repository setup, which meant that the ament plugin
  built the ROS2 underlay from source, and it was predictably dreadfully slow. However,  […]
url: https://canonical.com/blog/building-ros2-snaps-with-colcon?format=md
---

1. [Blog](https://canonical.com/blog)
2. Article

---

[Kyle Fazzari](https://canonical.com/blog/author/kyrofa "More about Kyle Fazzari")

28 February 2019

# Building ROS 2 snaps with Colcon

[robotics](https://canonical.com/blog/tag/robotics)
[ROS](https://canonical.com/blog/tag/ros)
[snapcraft.io](https://canonical.com/blog/tag/snapcraft-io)
[Snaps](https://canonical.com/blog/tag/snaps)
[Ubuntu Core](https://canonical.com/blog/tag/ubuntu-core)

---

Share the article
> Please note that this blog post has out technical information that may no longer be correct. For latest updated documentation about robotics in Canonical please visit <https://ubuntu.com/robotics/docs>.

The snapcraft CLI has supported building ROS1 snaps for a while via the **catkin** plugin. We supported the ROS2 betas via the **ament** plugin, but that was before Open Robotics had a ROS2 package repository setup, which meant that the **ament** plugin built the ROS2 underlay from source, and it was predictably dreadfully slow. However, the stable releases of ROS2 introduced a new build system called **colcon**, and also had their own package repositories setup. Version 3.2 of the snapcraft CLI (just released today) sees the addition of a **colcon** plugin to support those new releases, and I’d like to give you a quick run-through of its capabilities.

### Prerequisites

In order to follow along, make sure you have at least v3.2 of the snapcraft CLI. The best way to do that is to install the snap:

```
$ sudo snap install --classic snapcraft
```

Some previous experience [building a snap](https://snapcraft.io/) will also be helpful.

### Let’s get started

#### Create the snapcraft.yaml

First of all, create a new directory and initialize it with a **snapcraft.yaml**:

```
~$ mkdir ros2-snap
~$ cd ros2-snap/
~/ros2-snap$ snapcraft init
Created snap/snapcraft.yaml.
Go to https://docs.snapcraft.io/the-snapcraft-format/8337 for more information about the snapcraft.yaml format.
```

Open that **snap/snapcraft.yaml** file, and make it look like this:

```
name: ros2-talker-listener
version: "0.1"
summary: ROS2 Talker/Listener Example
description: |
  This example launches a ROS2 talker and listener.

grade: devel
confinement: strict
base: core18

parts:
  ros-demos:
    plugin: colcon
    source: https://github.com/ros2/demos.git
    source-branch: crystal
    colcon-rosdistro: crystal
    colcon-source-space: demo_nodes_cpp
    build-packages: [make, gcc, g++]
    stage-packages: [ros-crystal-ros2launch]

apps:
  run:
    command: opt/ros/crystal/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py
    plugs: [network, network-bind]
```

Let’s break that down and go through it by section.

```
name: ros2-talker-listener
version: "0.1"
summary: ROS2 Talker/Listener Example
description: |
  This example launches a ROS2 talker and listener.
```

This is the basic metadata required by all snaps. These fields are fairly self-explanatory, but note that the **name** must be globally unique among all snaps. You might consider appending your developer name to the end of the snap name, for example.

```
grade: devel
confinement: strict
base: core18
```

**grade** can be either **stable** or **devel**. If it’s **devel**, the store will prevent you from releasing into one of the two stable channels (**stable** and **candidate**, specifically). If it’s **stable**, you can release it anywhere.

**confinement** can be **strict**, **devmode**, or **classic**. **strict** enforces confinement, whereas **devmode** allows all accesses, even those that would be disallowed under **strict** confinement, and logs access that would be disallowed. **classic** is even less confined than **devmode** in that it doesn’t even get private namespaces anymore (among other things). There is [more extensive documentation](https://docs.snapcraft.io/snap-confinement/6233) on confinement available.

As I’ve said in the past, I typically use **strict** confinement unless I know for sure that the thing I’m snapping won’t run successfully under confinement, in which case I’ll use devmode. I typically avoid classic unless I never intend for the snap to run confined (e.g. you’ll notice the snapcraft CLI is a classic snap, since it needs more access to the host than confinement would allow).

Finally, the **base** keyword specifies a special kind of snap that provides a minimal set of libraries common to most applications (e.g. libc). It will be the root filesystem for this snap. In this case, we’re using **core18** which is a minimal rootfs based upon Ubuntu Bionic (18.04).

```
parts:
  ros-demos:
    plugin: colcon
    source: https://github.com/ros2/demos.git
    source-branch: crystal
    colcon-rosdistro: crystal
    colcon-source-space: demo_nodes_cpp
    build-packages: [make, gcc, g++]
    stage-packages: [ros-crystal-ros2launch]
```

The snapcraft CLI is responsible for taking many disparate parts and orchestrating them all into one cohesive snap. You tell it the parts that make up your snap, and it takes care of the rest. Here, we’re saying that we have a single part called **ros-demos**. We specify that it builds using the **colcon** plugin, and we point it to the ROS2 demos GitHub repository (this could just as easily be a directory on disk). We also specify that we’re using the newest ROS2 release as of this writing: Crystal. We point the **colcon** plugin at the C++ demo nodes specifically. We also provide a list of packages that need to be installed in order to build (**build-packages**), and also ask that **ros-crystal-ros2launch** gets staged into the snap alongside the rest of the part to be used at runtime (specifically, we’ll use it in the app, below). To view all the options supported by the **colcon** plugin, run the command **snapcraft help colcon**.

```
apps:
  run:
    command: opt/ros/crystal/bin/ros2 launch demo_nodes_cpp talker_listener.launch.py
    plugs: [network, network-bind]
```

This part is interesting. When we build this snap, it will include a complete ROS2 system: rclcpp, the demo\_nodes\_cpp workspace, etc. It could contain the entire system necessary for a robot in one installable blob. It’s a standalone unit: we’re in total control of how we want our users to interact with it. We exercise that control via the **apps** keyword, where we expose specific commands to the user. Here we specify that this snap has a single app called **run**. The command that this app actually runs within the snap uses the **ros-crystal-ros2launch** that we staged to fire up the demo nodes’ talker/listener launch file. Finally, we use **plugs** to specify that this app requires network access ([read more about interfaces](https://docs.snapcraft.io/interface-management/6154)).

#### Build the snap

Now that we’ve defined the **snapcraft.yaml**, it’s time to build the snap. Make sure you’re in the directory we created earlier (the one that contained the **snap/** directory), and run **snapcraft**:

```
~$ cd ros2-snap/
~/ros2-snap$ snapcraft
<snip>
Snapped ros2-talker-listener_0.1_amd64.snap
```

Note that depending on your host and whether or not you’ve built snaps in the past, the snapcraft CLI may prompt you to install Multipass, a tool used by the snapcraft CLI to manage VMs for building snaps.

The build process will take a few minutes. You’ll see the snapcraft CLI fetch **rosdep**, which is then used to determine the dependencies of the packages in the **colcon** workspace. It then pulls those down and unpacks them into the snap, and finally builds the packages in the workspace and installs them into the snap as well. At the end, you’ll have your snap.

#### Test the snap

Let’s install the snap we just built:

```
~/ros2-snap$ sudo snap install --dangerous ros2-talker-listener_0.1_amd64.snap

ros2-talker-listener 0.1 installed
```

Note the use of the **–dangerous** flag. That’s required because we’re installing a snap from disk instead of using the store, and snapd (the daemon with which we’re communicating using the **snap** command) only trusts snaps that it can cryptographically verify as being from the store unless we tell it otherwise with this flag.

Finally, let’s run the app we defined in the **snapcraft.yaml**:

```
$ ros2-talker-listener.run
[INFO] [launch]: process[talker-1]: started with pid [26273]
[INFO] [launch]: process[listener-2]: started with pid [26274]
[INFO] [talker]: Publishing: 'Hello World: 1'
[INFO] [listener]: I heard: [Hello World: 1]
[INFO] [talker]: Publishing: 'Hello World: 2'
[INFO] [listener]: I heard: [Hello World: 2]
```

As you can see, it works great. You could hand this snap to anyone with a snap-capable system, even if they don’t have ROS installed, and it would work exactly the same way for them.

I hope this gives you a decent overview of the capabilities of the **colcon** plugin, and I look forward to seeing what you do with it! Please feel free to ask any questions here, on the [Snapcraft forums](https://forum.snapcraft.io/), or on the [ROS forums](https://discourse.ros.org/). I’d love to hear any feedback you have.

*This article originally appeared on* [*Kyle Fazzari’s blog*](https://kyrofa.com/posts/building-ros2-snaps-with-colcon)*.*

[Get in touch

Interested in running Ubuntu in your organization?](https://ubuntu.com/about/contact-us/form)

## Sign up for our newsletter

Get the latest Canonical news and updates in your inbox.

Work email:

\*I agree to receive information about Canonical's
products and services.

By submitting this form, I confirm that I have read and agree to [Canonical's Privacy Policy](https://canonical.com/legal/dataprivacy).

Sign up

## Share on

---

## Related posts

[### Extending ROS Noetic Support with ESM-Enabled Content Snaps](https://canonical.com/blog/extending-ros-noetic-support-with-esm-enabled-content-snaps)

Canonical has now extended its ESM (Expanded Security Maintenance) for ROS coverage to ROS Noetic content-sharing snaps. With ESM for ROS now available in both deb and snap...

[Florencia Cabral Berenfus](https://canonical.com/blog/author/flor-cabral)

17 December 2025

[### Discover your fully open source robotics observability at ROSCon 2025](https://canonical.com/blog/roscon-2025)

Another year, another ROSCon! This year we’re heading to Singapore, and Canonical is once again thrilled to sponsor this important community event. Just like last year in...

[Gabriel Aguiar Noury](https://canonical.com/blog/author/g-aguiar-noury)

22 October 2025

[### Canonical is now a platinum member in the Open Source Robotics Alliance](https://canonical.com/blog/osra-membership)

Ubuntu is the home of ROS. The very first ROS distribution, Box Turtle, launched on Ubuntu 8.04 LTS, Hardy Heron, and since then, Ubuntu and ROS have grown hand in hand. With...

[Gabriel Aguiar Noury](https://canonical.com/blog/author/g-aguiar-noury)

20 August 2025

[### So you need to add microcontrollers to your fleet: now what?](https://canonical.com/blog/microcontrollers-ubuntu-core-golioth)

Your Ubuntu Core fleet is running beautifully. OTA updates roll out in minutes. Every device is strictly confined, cryptographically attested, and carrying a 10 to 15 year long...

[Jonathan Beri](https://canonical.com/blog/author/jmberi)

18 June 2026
