Make an X11-based Kiosk Snap

1. Overview

What you’ll learn

In this tutorial we will create a snap of an X11 application to act as the graphical user interface for an IoT or kiosk device. For the introduction to this tutorial series and the Mir display server please visit here.

X11 is a legacy protocol, it is known to be insecure, so we need to take steps to ensure it is secured correctly. To do this we shall embed an intermediary X server based on Mir in the application snap and use snapd’s infrastructure to maintain security.

The combination of Snap, the Ubuntu Frame Wayland server and Ubuntu Core ensures the reliability and security of any graphical embedded device application.

This tutorial assumes you are familiar with the material in Make a Wayland-native Kiosk snap. In particular, techniques for debugging problems in your snap are not repeated here.

Depending on the toolkit your application is written in, it may work on the newer and more secure Wayland protocol. If so, the snapping process is simpler. To check, please read this guide.

What you’ll need

  • An Ubuntu desktop running any current release of Ubuntu, or an Ubuntu Virtual Machine on another OS.
    • Note: this tutorial assumes you are running an amd64 distribution on the device you’re using to follow this tutorial. There will be minor differences in some file names if you are running a different architecture.
  • A ‘Target Device’ from one of the following:
    • A device running Ubuntu Core 18.
      This guide shows you how to set up a supported device. If there’s no supported image that fits your needs you can create your own core image.
    • Using a Virtual Machine (VM) You don’t need to have a physical “Target Device”, you can follow the tutorial with Ubuntu Core in a VM. This guide shows you how to set up an Ubuntu Core VM.
    • Using Ubuntu Classic You don’t have to use Ubuntu Core, you can use also a “Target Device” with Ubuntu Classic. Read this guide to understand how to run kiosk snaps on your desktop, as the particular details won’t be repeated here.

2. X11 on top of Wayland

We use Wayland as the primary display interface. We will use Mir to manage the display and support connections from Wayland clients and Snapd will confine the applications and enable Wayland protocol interactions through Mir, securely.

Wayland is relatively new in the Linux world however. Its predecessor - X11 - has been the dominant graphics technology for decades. As a result, not all toolkits have native support for Wayland - they only support X11.

To deal with the limitations of X11 in a secure fashion we will embed a tiny intermediary X11 server called “Xwayland” inside the application snap, which translates the X11 calls to Wayland ones, allowing the X11 application to talk Wayland - a far more secure protocol.

The Snap security framework then ensures this X11 server is private to the application snap.


Toolkits without Native support for Wayland

  • GTK2
  • Qt4
  • Java apps
  • Windows apps emulated under Wine

It may also be your application is written using X11 calls directly, in which case this guide is for you.

If your application is written using GTK3/4, Qt5 or SDL2, or another toolkit with native Wayland support, you should follow this guide.

3. Introducing glxgears and Mir support for X11

A large fraction of applications are still written for X11 - there are those written with Qt4 and Gtk2, but also Java, Mono or Wine-based. We can snap these for a kiosk just fine, we just need to add some extra bits to the snap.

We’ll take a trivial example to start with (glxgears). glxgears is again a handy snap to have, as it will help prove OpenGL is working for X11 apps inside Ubuntu Core.

4. Preparation

Install Ubuntu Frame

We will do our initial development and testing on your Linux desktop, and for this you should install ubuntu-frame:

sudo snap install ubuntu-frame

To build snaps, you need to install snapcraft:

sudo snap install snapcraft --classic

and install Multipass:

sudo snap install multipass

5. Set up test & development environment

As you may be running a Wayland based desktop and we want to run your application against Ubuntu Frame, we need to set up a development environment for this. Open a terminal and type:

export WAYLAND_DISPLAY=wayland-99

This will open a “Mir-on-X” window with Ubuntu Frame running in it.

Why are we changing the WAYLAND_DISPLAY environment variable? It’s because newer versions of Ubuntu use Wayland. If we run Ubuntu Frame without this, it will try to bind to the default, wayland-0, which is already being used by your desktop. We assume that wayland-99 is definitely not in use. Unless you happen to be running 100 displays for some reason…

You should follow the tutorial using this terminal and this “Mir on X” window. If you close them, or open another terminal to work in remember to export WAYLAND_DISPLAY=wayland-99 and, if necessary, restart Ubuntu Frame.

6. First Pass Snapping: Test on Desktop

For our first pass we will snap glxgears and run it on our Ubuntu desktop. This guide assumes you are familiar with creating snaps. If not, please read here first.

Create the snap directory by forking

git clone

Change to the new mir_kiosk_x11-example directory.

cd mir_kiosk_x11-example

Update the “snap/snapcraft.yaml” file with as follows…

Update the metadata:

@@ -1,7 +1,8 @@
-name: mir-kiosk-x11-example     # YOUR SNAP NAME GOES HERE
-version: '0.1'                  # YOUR SNAP VERSION GOES HERE
-summary: example X11 kiosk      # YOUR SUMMARY GOES HERE
-description: example X11 kiosk  # YOUR DESCRIPTION GOES HERE
+name: mir-kiosk-x11-example
+version: '0.1'
+summary: example (glxgears) X11 kiosk, using mir-kiosk-x11
+description: |
+  example (glxgears) X11 kiosk, using mir-kiosk-x11

Add “glxgears” to the app command:

@@ -12,7 +13,7 @@
-    command: usr/local/bin/x11_kiosk_launch ### YOUR COMMAND GOES HERE
+    command: usr/local/bin/x11_kiosk_launch glxgears

Add the packaging commands for glxgears:

-  your-part:
+  glxgears:
     plugin: nil
+    stage-packages:
+      - mesa-utils
     stage-snaps: [mir-kiosk-x11]

Here’s the full snapcraft.yaml for reference:

name: mir-kiosk-x11-example
version: '0.1'
summary: example (glxgears) X11 kiosk, using mir-kiosk-x11
description: |
  example (glxgears) X11 kiosk, using mir-kiosk-x11
base: core20
confinement: strict
grade: devel

#    daemon: simple
#    restart-condition: always
      - env-setup
    command: usr/local/bin/x11_kiosk_launch glxgears
      - opengl         # For Mir
      - wayland        # For Mir
      - network-bind   # For Mir (to serve X11)

  - build-on: amd64
  - build-on: arm64
  - build-on: armhf

    plugin: nil
      - mesa-utils
    stage-snaps: [mir-kiosk-x11]

    bind: $SNAP/usr/share/X11
    symlink: $SNAP/usr/bin/xkbcomp
    bind: $SNAP/usr/share/icons
    bind: $SNAP/usr/share/fonts
    bind: $SNAP/etc/fonts

Create the snap by running


If your device uses the aarch64 architecture, you will need to add the --use-lxd flag when running snapcraft.

You should be left with a “mir-kiosk-x11-example_0.1_amd64.snap” file.

Let’s test it!

sudo snap install --dangerous ./mir-kiosk-x11-example_0.1_amd64.snap
snap connect mir-kiosk-x11-example:wayland ubuntu-frame:wayland

You should see a fullscreen gear animation in the Mir-on-X window.

7. Snapping to use on a device

Now make another update to your snapcraft.yaml file:

-#    daemon: simple
-#    restart-condition: always
+    daemon: simple
+    restart-condition: always

This changes the snap from a simple command to a “daemon” which is how thing are usually run on devices. Check this builds locally before proceeding:


8. Building snaps for different architectures

Your device is likely not the same architecture as your desktop, so you need to build your snap for that architecture. You can do this using remote-build:

snapcraft remote-build

Depending on the load on the builders this can take a few minutes to complete. Once it completes you should have an suitable .snap for your device architecture. (We’ll assume that is “armhf” for these notes, but you can replace that with whatever suits your case best.)

9. Second Pass Snapping: Your Device

Device Setup

Open another terminal and ssh login to your device and from this login install the Ubuntu Frame snap.

snap install ubuntu-frame

It auto-starts, so now you should have a graduated grey screen.

Ubuntu Frame provides the graphical environment needed for running a graphical snap.

10. Deploy the snap on the device

Push the snap you built to your device using your device’s SSH username & IP address details:

scp mir-kiosk-x11-example_0.1_armhf.snap <user>@<ip-address>:~

We now have the .snap file on the device in its home directory. We need to install the snap, configure it to talk Wayland to mir-kiosk and run the application. In your ssh session to your device:

snap install --dangerous ./mir-kiosk-x11-example_0.1_armhf.snap

On your device, you should see the same graphical animations you saw earlier. It will continue to run until you run “snap stop mir-kiosk-x11-example

Your device is now a kiosk! Rebooting will restart Ubuntu Frame and mir-kiosk-x11-example automatically.

Should you wish to share this snap, the next step would be to push your snap to the Snap Store. And, once you are satisfied that the snap is working well from the store, you should change the grade to stable so that you can publish to the “stable” channel:

- grade: devel
+ grade: stable

11. Congratulations

Congratulations, you have created your first graphical snap of an X11 app for Ubuntu Core.

1 Like

How is this intended to differ from

It seems confusing to have both.

1 Like