Overview
What Is It?
Ubuntu Core is an OS intended for use on devices embedded within commercial products or industrial equipment. It's very locked down by default. It runs on lots of different hardware. This guide focuses on Raspberry Pi 3, 4, and 5 devices.
It is a very different kind of OS than the traditional Raspberry Pi OS, which is aimed at students, hobbyists, and tinkerers. The locked down nature can make the development iteration cycle slower and more tedious than traditional Pi OS.
Ubuntu Core's strengths really shine most after you've already got a project functioning how you want under a more traditional OS like Pi OS or Ubuntu Server/Desktop and you are ready to deploy somewhere remote.
The Ubuntu Core documentation describes the OS like this:
Ubuntu Core is an immutable and transaction-based version of Ubuntu that’s engineered for cloud, embedded, and IoT systems.
It provides an image-based deployment infrastructure with automatic updates for sandboxed applications, enabling the creation of production-ready systems with minimal attack surface and automatic rollback capabilities.
Ubuntu Core reduces the time to production by eliminating manual provisioning, ensuring systems remain secure throughout their lifecycle, and enabling rapid updates across fleets of devices at scale.
It is designed for embedded Linux developers, IoT device manufacturers, cloud-based applications, and organizations deploying embedded systems in robotics, automotive, signage, industrial automation, and IoT applications - from single devices to thousands in the field.
Blinka Inside Ubuntu Core
The process to get Adafruit_Blinka, and a Python script that uses it, running inside of Ubuntu Core is quite different from what you might be familiar with. All libraries and code must be packaged inside of a Snap to be installed and run. It's not possible to use package manager tools like apt and pip directly. Requirements need to be declared and embedded during the Snap compilation, or "packing" step.
This guide will walk through the process of getting the Ubuntu Core loaded onto a Pi, show a demo of a basic sensor based project, and cover the steps required to build snaps and customized Ubuntu Core images.
Building Snaps and Ubuntu Core images needs to be done on another machine that is running Ubuntu. One option is a second Raspberry Pi flashed with Ubuntu Server or Desktop.
Page last edited June 08, 2026
Text editor powered by tinymce.
Ubuntu One Account & SSH Key
Ubuntu One Account
The standard way to manage and control Ubuntu Core devices is via SSH using public/private keys to authenticate. The user account on the device and the public SSH key are set up on first boot using an Ubuntu One account. You have to create an account and upload a public key using a browser on a different computer (not the Pi running Ubuntu Core). This documentation page contains detailed information about the account and SSH setup.
Start by going to login.ubuntu.com and selecting "I don’t have an Ubuntu One account" then fill in email, name, username, and password to create an account.
Create SSH Key
Creating an SSH key pair can be done easily on Linux or Mac. It's possible on Windows, but trickier. You can use another Raspberry Pi running Pi OS or Ubuntu Server OS to generate the SSH keys.
First create a directory to store keys in and restrict its permissions to be accessible only to the user that owns it with these commands.
mkdir -p ~/.ssh chmod 700 ~/.ssh
Use the following commands to change directories and start the key generation wizard.
cd ~/.ssh/ ssh-keygen -t rsa
Respond to the following prompts in the keygen wizard
- File to save the key in. Enter "id_ubuntucore" or whatever name you want to give your keypair
- Passphrase is an extra password that must be entered in order to use the key. It can be left blank, but it's best to enter a passphrase and keep it secret. It acts as a second factor of authentication beyond just having the key file.
- Confirm Passphrase by entering the exact same value again.
The output of the wizard will look something like this once complete:
Generating public/private rsa key pair. Enter file in which to save the key (/home/myuser/.ssh/id_rsa): id_ubuntucore Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in id_ubuntucore Your public key has been saved in id_ubuntucore.pub The key fingerprint is: SHA256:NdKl6pErAxt4fVDNsptgbYwN5B66ZncxWeRk6Zs0Xro user@hostname The key's randomart image is: +---[RSA 3072]----+ | .. .o+.. | | ....*+o | | +*.+B | | . ++o*B+.. | | . =.ooS= * | | . = oo== | | = + + . | | o . + E | | | +----[SHA256]-----+
That will create two files, id_ubuntucore and id_ubuntucore.pub if you used the name quoted above.
As the file extension suggests id_ubuntucore.pub is the public key of this key pair. It needs to be copied onto the device you want to access with SSH. For Ubuntu Core, the copying happens by uploading the public key to your Ubuntu One account and entering your email in the first boot config.
The file with no extension, id_ubuntucore, is the private key file. Keep that file secure. Anyone who has that file and its passphrase can log in to control any computers configured with the public key.
Upload Public SSH Key to Ubuntu One
Go to: https://login.ubuntu.com/ssh-keys and use the import key section.
Open the id_ubuntucore.pub file in your text editor of choice or use cat in the terminal to print it. Copy the entire contents of the public key file and paste them into the import box on the ssh-keys page then click the Import SSH Key button.
Page last edited June 08, 2026
Text editor powered by tinymce.
Install
The steps to install the pre-built images for Raspberry Pi are documented here in the Ubuntu Core docs.
Prepare SD Card
Flashing an SD card with an Ubuntu Core image is largely the same process as flashing Pi OS or any other image. Use the Raspberry Pi Imager application.
Select the appropriate model of your device.
Click on Other general-purpose OS in the main OS menu.
Click on Ubuntu in the next OS menu.
Scroll to the bottom of the list and click on Ubuntu Core24.
Click Next and continue through the rest of the Imager wizard as normal to write the image to the SD card.
When the flashing and verifying is complete, click Finish and then unmount or eject the SD card from your computer.
First Boot
After flashing the SD card, insert it into the Pi and then connect the Pi's power adapter. The first boot up will take several minutes and will reboot automatically during the process. When it's complete, it will prompt with Press enter to configure.
This initial part of the configuration needs to be done with a keyboard and HDMI display connected to the Raspberry Pi.
Network
Press the enter key a few times to get past the initial prompts and get into the network configuration.
On the network config screen use arrow up/down to move the selector around and highlight either wlan0 or eth0 based on how you want to connect your network. Press enter on the desired network interface.
To set up a WIFI network, select Edit Wifi in the wlan0 sub menu.
Then use arrow up/down to navigate around the WiFi dialog box. Either enter your SSID and password directly, or select Choose a visible network, find your network in the list and enter the password.
Highlight Save and press enter when complete.
It will take a few seconds for the network connection to activate, a rotating slash character indicates progress while it's working.
Once the connection is made successfully, the first item in the menu at the bottom of the screen will change to Done. Highlight it and press enter to move to the next step.
Profile
The last step of the first boot process is entering the email address associated with your Ubuntu One account. This should be the same email account that was set up on the prior page and has your public SSH key uploaded to it.
When you enter your email address on this page, it is going to try to download the public SSH key(s) from your account. You have to have the keys uploaded prior to this step during the first boot, so if you haven't uploaded them, go do it now on a different computer before entering your email here.
After entering your email address, use the arrow keys to highlight the Done item in the menu at the bottom of the screen and press enter
Once complete, it will show a message like this on the screen.
This device is registered to [ubuntu-sso-email-address] Remote access was authenticated with SSO user [username]. Public SSH keys were added to the device for remote access. [ubuntu-sso-email-address] can connect remotely to this device via SSH: ssh [username]@[ip-address]
The default screen shown, when the device boots up from here on after, will look similar and will show the IP address with SSH command to connect to the device.
Verify that the SSH connection works, with these commands, on another computer on the same network as the Pi. Fill in your own username and IP address to the relevant spots. Enter the same passphrase used when creating the key.
# Activate the key in your terminal session ssh-add ~/.ssh/id_ubuntucore ssh [username]@[ip-address] # OR specify the keyfile with argument ssh -i ~/.ssh/id_ubuntucore [username]@[ip-address]
If SSH connects successfully, then the installation is done and you're ready to move on to the Blinka demo. If it doesn't, then pause here and troubleshoot it. Look at the display to identify any errors. If it does not show a valid network connection, use a keyboard to troubleshoot/configure the network.
Page last edited June 08, 2026
Text editor powered by tinymce.
Sensor Dashboard Demo
This page demonstrates an environmental sensor dashboard. The demo uses ubuntu-frame and wpe-webkit-mir-kiosk to automatically launch a full-screen headless browser window to display the dashboard. Adafruit Blinka allows the app to read environmental data from the BME280 sensor over I2C.
snap install ubuntu-frame wpe-webkit-mir-kiosk
The HDMI display will start showing a default web page when the kiosk app install completes.
BME280 Dashboard
The BME280 dashboard app is a locally built snap file. It is not distributed through the snap store like ubuntu-frame and wpe-webkit-mir-kiosk. The code for this snap can be found on the Building Snaps page of this guide.
Download & Copy Snap
Use the button below to download a copy of the file bme280-dashboard_0.1_arm64.snap.
Copy the file to the Ubuntu Core device. One easy way to copy it is scp.
# Run on your main computer to copy the snap file to the Ubuntu Core device scp -i [keyfile] bme280-dashboard_0.1_arm64.snap [username]@[hostname-or-ip]:/home/[username]/ # example: scp -i ~/.ssh/id_ubuntucore bme280-dashboard_0.1_arm64.snap [email protected]:/home/foamyguy/
Install Snap
Install the snap file with the following command. The --dangerous flag is required to install snap files that are not distributed through the snap store. The --devmode flag loosens some of the restrictions within the OS for development. It allows the Pi 5's GPIO access to work without further customization of the system image. It's required on Pi 3 and 4 as well because they all use the same Snap for this demo. It would be possible to modify and rebuild the snap to target only Pi 3 or 4 to avoid needing the --devmode flag.
sudo snap install ./bme280-dashboard_0.1_arm64.snap --dangerous --devmode
Connect Plugs to Slots
Ubuntu Core apps are sandboxed and locked down by default, with minimal access to the hardware on the device. The permission model is based on plugs and slots. If an application wants to use a particular piece of hardware on the device, the app declares a plug for the hardware. The OS provides slots that the plugs need to be connected to in order to actually be granted access to the hardware.
For testing a custom built snap, the only way to connect the plug to the slot is with a command. For building a custom image and deploying, it is possible to use a customized gadget snap to make these connections automatically upon first boot up. A custom gadget snap is also what would be required to target the Pi 5 with this snap and remove the need for the --devmode installation flag.
Run these commands to allow permission for the i2c bus and hardware-observe plugs, both are required by the Blinka demo.
sudo snap connect bme280-dashboard:i2c pi:i2c-1 sudo snap connect bme280-dashboard:hardware-observe
Test Script
To confirm everything is working, use the sudo bme280-dashboard.blinka-test command. You should see output similar to this.
Press ctrl-c to stop the program.
Set Dashboard URL
Finally, configure the kiosk URL to point to the dashboard, which is being hosted by a small webserver inside the snap on localhost port 8080.
snap set wpe-webkit-mir-kiosk url=http://localhost:8080/
The display will change from the default Ubuntu Core web page to the sensor dashboard demo.
Page last edited June 08, 2026
Text editor powered by tinymce.
Building Snaps
Development of apps to run under Ubuntu Core is different than developing for other operating systems. All required libraries and code must be collected together and compiled into a Snap. If you want to customize the dashboard demo from the previous page by changing it visually, or swapping to another sensor, you have to rebuild and re-install the snap.
This page in the Ubuntu docs is a tutorial covering the entire set up and creating a Snap from scratch. This guide page will cover the necessary set up and how to build the sensor dashboard demo project from the previous page.
The build needs to occur on a device with the same architecture as the target. For example, if you want the app to run under Ubuntu Core x64 on a Raspberry Pi, then you need to build the Snap on an arm64 based device. Advanced users can build inside a virtual machine. This guide documents building using a separate Raspberry Pi that is running the Ubuntu Server OS.
In the Raspberry Pi Imager: Select the appropriate model of your device.
Click on Other general-purpose OS in the main OS menu.
Click on Ubuntu in the next OS menu.
Scroll to the bottom of the list and click on Ubuntu Server 24.04 LTS (64-bit).
Click Next and continue through the rest of the Imager wizard as normal to write the image to the SD card.
When the flashing and verifying is complete click Finish and then unmount or eject the SD card from your computer. Insert it into a Raspberry Pi and boot up.
sudo apt update sudo apt upgrade
sudo snap install snapcraft --classic sudo snap install lxd
Add your user to the lxd group and then reboot the Pi so that the new groups take effect.
sudo usermod -a -G lxd $USER sudo reboot
After the system boots back up use this command to initialize lxd.
sudo lxd init --auto
Click the button above to download the BME280 dashboard snap project code. Unzip it and copy the bme280_dashboard_snap/ folder inside to the build machine.
Snapcraft.yml
All snaps must have a snapcraft.yml file. It defines the requirements and behaviors of the application. This docs page breaks down the structure and typical contents of the file.
Here is the snapcraft.yml file for the BME280 dashboard demo.
# SPDX-FileCopyrightText: 2026 Tim Cocks for Adafruit Industries
# SPDX-License-Identifier: MIT
name: bme280-dashboard
base: core24
version: '0.1'
summary: BME280 environmental sensor dashboard demo for Blinka on Ubuntu Core
description: |
A learning snap that bundles Adafruit Blinka and exposes
the hardware interfaces needed to talk to I2C/GPIO/SPI.
Includes a kiosk-style web dashboard fed by a BME280
temperature/humidity/pressure sensor.
grade: devel
# devmode (not strict): the Pi 5 lgpio backend opens the raw /dev/gpiochip4
# (RP1) char device at "import board", which no snapd interface
# (gpio / gpio-memory-control) grants. devmode lets it through. Pi 3/4 work
# under strict because RPi.GPIO uses /dev/gpiomem via gpio-memory-control.
confinement: devmode
apps:
blinka-test:
command: bin/test-sensor
plugs:
- hardware-observe
- i2c
- spi
- serial-port
- gpio
- gpio-memory-control
sensor-service:
command: bin/sensor-service
daemon: simple
install-mode: enable
restart-condition: always
restart-delay: 5s
plugs:
- hardware-observe
- i2c
- spi
- gpio
- gpio-memory-control
sensor-web:
command: bin/sensor-web
daemon: simple
install-mode: enable
restart-condition: always
restart-delay: 5s
plugs:
- network
- network-bind
parts:
app:
plugin: python
source: .
python-packages:
- adafruit-blinka
- RPi.GPIO # Pi 3/4 (bcm283x/bcm2711) GPIO backend
- lgpio # Pi 5 (bcm2712) GPIO backend - Blinka imports this on the Pi 5
- adafruit-circuitpython-bme280
- flask
- waitress
stage-packages:
- i2c-tools
- libgpiod2
- python3-libgpiod
build-packages:
- libgpiod-dev
override-build: |
craftctl default
install -D -m 0755 test-sensor $CRAFT_PART_INSTALL/bin/test-sensor
The file defines 3 apps:
-
blinka-testa wrapper around the BME280 simpletest script. Useful to confirm that Blinka and the sensor hardware are operational. -
sensor-servicea background service that reads data from the sensor periodically and saves it to a sqlite3 database. This command uses the sensor_kiosk/sensor_service.py script. -
sensor-weba background service that runs a Flask web server that hosts the dashboard. The server code is located in sensor_kiosk/app.py.
The "parts" section defines all of the requirements for the Snap. The sensor demo requires Blinka, its GPIO dependencies, the adafruit_bme280 library, Flask, and waitress.
snapcraft pack
Once the build is complete it will output a message like this.
Packed bme280-dashboard_0.1_arm64.snap
Copy the resulting snap file to the Ubuntu Core device using scp or your preferred file transfer method.
scp -i [keyfile] bme280-dashboard_0.1_arm64.snap [username]@[hostname-or-ip]:/home/[username]/ # example: scp -i ~/.ssh/id_ubuntucore bme280-dashboard_0.1_arm64.snap [email protected]:/home/foamyguy/
From here, everything works the exact same as is documented starting with the Install Snap section on the dashboard demo page.
Page last edited June 08, 2026
Text editor powered by tinymce.
Custom Images
This page from the Ubuntu Core docs walks through all of the steps necessary to build an image. This guide page will cover the process of creating an image that has the BME280 dashboard preloaded into it.
Images can be created using Ubuntu Server or Desktop 22.04+ OS. The Raspberry Pi that was set up with Ubuntu Server on the Building Snaps page is suitable if it has a large enough SD card. The docs list 10gb requirement for the build machine. The output image file alone is about 3.8gb.
Images are built from models. A model is a JSON file that acts as a recipe for an image. It defines the Snaps built-in to the image, and metadata including a developer ID from a Ubuntu One account.
Retrieve Developer ID
You can find your developer ID using the snapcraft CLI. It's the same CLI used to build Snaps, so if you followed along with that page, you should have it already. If you don't then install it with this command.
sudo snap install snapcraft --classic
Next, use these commands to login and export your credentials to an environment variable that the snapcraft CLI will use.
snapcraft export-login credentials.txt export SNAPCRAFT_STORE_CREDENTIALS=$(cat credentials.txt)
You should see output like this.
Now use snapcraft whoami to find your developer ID.
snapcraft whoami
Look for the row that starts with id:, the value to the right is your developer ID. Copy it and save it somewhere to reference later. The model JSON file will need to have your ID put into it.
Model JSON
This is the model JSON file for an image with ubuntu-frame and wpe-webkit-mir-kiosk built in. To build an image from the model, fill in your developer ID for the authority-id and brand-id values.
{
"type": "model",
"series": "16",
"model": "ubuntu-core-24-pi-arm64",
"architecture": "arm64",
"authority-id": "[developer_id_here]",
"brand-id": "[developer_id_here]",
"timestamp": "[timestamp_here]",
"base": "core24",
"grade": "dangerous",
"snaps": [
{
"name": "pi",
"type": "gadget",
"default-channel": "24/stable",
"id": "YbGa9O3dAXl88YLI6Y1bGG74pwBxZyKg"
},
{
"name": "pi-kernel",
"type": "kernel",
"default-channel": "24/stable",
"id": "jeIuP6tfFrvAdic8DMWqHmoaoukAPNbJ"
},
{
"name": "core24",
"type": "base",
"default-channel": "latest/stable",
"id": "dwTAh7MZZ01zyriOZErqd1JynQLiOGvM"
},
{
"name": "snapd",
"type": "snapd",
"default-channel": "latest/stable",
"id": "PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4"
},
{
"name": "console-conf",
"type": "app",
"default-channel": "24/stable",
"id": "ASctKBEHzVt3f1pbZLoekCvcigRjtuqw"
},
{
"name": "mesa-2404",
"type": "app",
"default-channel": "latest/stable",
"id": "HyhSEBPv3vHsW6uOHkQR384NgI7S6zpj"
},
{
"name": "mesa-core22",
"type": "app",
"default-channel": "latest/stable",
"id": "UijXdFgvIKp9ZZ6P4ijPAJHWZLtSKgWm"
},
{
"name": "ubuntu-frame",
"type": "app",
"default-channel": "24/stable",
"id": "BPZbvWzvoMTrpec4goCXlckLe2IhfthK"
},
{
"name": "core22",
"type": "base",
"default-channel": "latest/stable",
"id": "amcUKQILKXHHTlmSa7NMdnXSx02dNeeT"
},
{
"name": "wpe-webkit-mir-kiosk",
"type": "app",
"default-channel": "22/stable",
"id": "01sV9tv4UTUQTU3jYsAF1gJ5qv7ZqGls"
}
]
}
Create & Register Key
Before the image can be made, the model must be signed to turn it into a model assertion. To sign the model, you need to create and register a key in the Ubuntu snapcraft system. This page documents the key set up and signing process.
If you've done any Ubuntu or Snap development before, you can check whether you have a key already with this command.
snapcraft list-keys
If you see "No keys have been registered..." then use the following commands to create and register a key. You can use any name that you like for the key.
# command snapcraft create-key [key-name] # example snapcraft create-key bme280-dashboard-key
You will be prompted for a passphrase to use on the key. This is an extra password that will be required in order to use the key. It can be left blank, but it's best to use a secure passphrase and keep it secret. It serves as a second factor in addition to possession of the key file. Enter the desired passphrase and repeat it again for the confirmation prompt.
# command snapcraft register-key [key-name] # example snapcraft register-key bme280-dashboard-key
You will be prompted for the passphrase in order to register the key, and again when you use it to sign a model.
Update Model Timestamp
Inside of the model JSON, the timestamp field needs to contain a timestamp that is newer than the signing key. So if you just created and registered a key using the steps above, then you must set the timestamp to a more recent value after the key was registered.
You can get a timestamp with suitable syntax using this command.
date -Iseconds --utc
Then update the value in the model JSON file with it.
"timestamp": "[timestamp_here]", # example "timestamp": "2026-06-03T20:27:10+00:00",
# command snap sign -k [key-name] [model-file.json] > [output_model-file.model] # example snap sign -k bme280-dashboard-key bme280_dashboard_core24_model.json > bme280_dashboard_core24_model.model
You will be prompted for the passphrase when you use a key to sign a model.
It will create a new file for the model assertion with the specified output name, bme280_dashboard_core24_model.model in the example above.
Build Image
Building the image is done with the ubuntu-image utility. Install it with this command.
sudo snap install ubuntu-image --classic --edge
Build the image from a model assertion with the following command. The --snap argument is optional. It allows you to specify a custom Snap file, such as one built on the previous page, to be included in the image. For final deployment, snaps would be distributed via the Snap Store instead.
# command ubuntu-image snap --allow-snapd-kernel-mismatch [model-assertion-file.model] --snap [custom.snap] #example ubuntu-image snap --allow-snapd-kernel-mismatch bme280_dashboard_core24_model.model --snap bme280-dashboard_0.1_arm64.snap
If successful, the output will look like this. It will also create a pi.img file as shown by the ls command after the output in the screenshot below.
Copy the resulting pi.img file to a computer with Raspberry Pi imager on it.
Flash this img file to an sdcard with Pi imager app.
Click Use custom on the OS menu and then select the pi.img file.
After flashing the image, follow the same steps from the First Boot Up section on the Install guide page. Once the first boot configuration is complete, you should be able to connect to the device with your SSH key.
Launch Dashboard
The Snaps required by the sensor dashboard demo are preloaded into this image, but the plugs still need to be manually connected to the slots in order for the sensor to function. The kiosk apps need to be set to auto-launch and have the URL configured for the dashboard also. Use the following commands to complete these steps.
# connect plugs for Blinka sudo snap connect bme280-dashboard:i2c pi:i2c-1 sudo snap connect bme280-dashboard:hardware-observe # Enable autolaunch and configure kiosk URL snap set ubuntu-frame daemon=true snap set wpe-webkit-mir-kiosk url=http://localhost:8080/
The sensor dashboard should load onto the screen after a moment.
Page last edited June 08, 2026
Text editor powered by tinymce.