Configure linux debian to boot into a fullscreen application

Table of contents

Linux is often used to build so-called "kiosk" systems; a configuration that will start a fullscreen application right after boot, without any user interaction or login required. This kind of configuration is common for DIY and low-cost devices, but is not as straight-forward as one may assume.

Creating a testing environment

Since testing configurations on a physical device can be cumbersome, this article will use vagrant and virtualbox to create a virtual machine to test the configuration and see it in action. Make sure you have both tools installed, then write the following content into a file named Vagrantfile:

Vagrant.configure("2") do |config|
 config.vm.box = "debian/bookworm64"
 config.vm.provider "virtualbox" do |vb|
   vb.gui = true
   vb.cpus = 2
   vb.memory = "2048"
 end
end

You can now start your virtual machine with

vagrant up

To interact with the virtual machine once it has been created, run

vagrant ssh

to obtain an SSH session as the vagrant user. This user is automatically created for you and already has sudo privileges.

When you are done testing, or want to start over, you can get rid of the virtual machine with

vagrant destroy -f

To get a new clean virtual machine, simply use vagrant up again.

Starting an app in kiosk mode

The guide assumes you have a linux debian installation without any desktop environment installed (if you're using the vagrant config above, you have exactly that).

Before starting, make sure the packages are up to date

sudo apt update -y && sudo apt upgrade -y

Next install the required packages:

sudo apt install -y xserver-xorg x11-xserver-utils xinit matchbox-window-manager chromium

Here is a brief explanation of what we need each for:

  • X server and tools (xserver-xorg, x11-xserver-utils, xinit): Handles the low-level basics for graphical applications, like displays, inputs and rendering.

  • Window manager (matchbox-window-manager): Manages windows on top of xorg, including features like size, placement and responding to fullscreen requests. We chose this window manager specifically because it is extremely lightweight and works well for kiosk mode setups.

  • Kiosk app (chromium): The application we want our system to boot into. Chromium is a simple browser that works well for displaying local or remote websites in kiosk mode, but you could use any other graphical application that has fullscreen support in it's place.



With these dependencies installed, it is time to configure the kiosk mode starting sequence. This requires a user that the system will automatically log in as, preferrably a newly created one for this specific purpose:

sudo useradd -m -s /bin/bash -p '' kioskuser

This creates a new user called kioskuser. They have a home created because we need to create init scripts there in a moment, use /bin/bash as their shell because we need .bashrc during startup, and have an empty password to allow passwordless login at boot.

The initial virtual terminal and login screen is handled by the getty service. In order to automatically log into our kiosk account at boot, we need to override the default parameters for it. Create the file /etc/systemd/system/getty@tty1.service.d/override.conf with adjusted ExecStart parameters:

sudo mkdir /etc/systemd/system/getty@tty1.service.d
sudo cat > /etc/systemd/system/getty@tty1.service.d/override.conf<< EOF
[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin kioskuser --noclear %I $TERM
EOF

Overriding the getty@tty1 service only applies our automatic login for the first virtual terminal tty1 (not others available through ctrl+alt+f2...f6, and not remote sessions like SSH). The first blank ExecStart= line is intentional and important, as it clears the previous contents inherited from the main getty service unit we are overriding. If it were missing, our arguments would be appended to the original line, not working as intended. If you named your kiosk user differently, make sure to adjust the last line after the --autologin flag to your custom username.

Now that the kioskuser is automatically logged in as at boot, we can append a small script to the end of their .bashrc file to start the xorg display manager for the tty1 session (and not for subsequent sessions or remote connections):

sudo cat >> /home/kioskuser/.bashrc<< EOF
if [ -z "\$DISPLAY" ] && [ "\$(tty)" = "/dev/tty1" ]; then
  startx
fi
EOF

The last task is to start the window manager and kiosk application once the xorg server is ready. We can simply write those commands to a .xinitrc file, which gets executed once the startx command completes:

cat > /home/kioskuser/.xinitrc<< EOF
matchbox-window-manager &
chromium --kiosk https://tech-couch.com
EOF
sudo chown kioskuser:kioskuser /home/kioskuser/.xinitrc

Note the ampersand & after the first command, ensuring the window manager starts in the background and does not block the chromium process.

Using the --kiosk flag for chromium will automatically start it in fullscreen and disable some UI elements, like tabs and the search bar the top. Instead of a remote URL, you could also provide a filepath to a local HTML file.

All that is left to do now is reboot the machine to see the configuration take effect:

sudo reboot

The virtual machine should now boot directly into the chromium application.

Fine-tuning chromium for kiosk mode

While supplying the --kiosk flag is a good start, there are other useful flags when running chromium in kiosk mode:


  • --no-first-run suppresses the first-run dialog (asking to sign in / sync profiles)

  • --incognito ensures browsing history, cookies and site data are not stored, since kiosk applications have to use for these features.

  • --disable-restore-session-state prevents chromium from showing a "restore closed tabs" page after unexpected shutdown, like power loss. Since kiosk mode devices are often turned off by unplugging, this feature may interfere with normal operation.

  • --disable-infobars hides infobars like the "Chrome is being controlled by automated test software" message that might appear when instrumenting the browser with WebDriver or debugging tools

  • --disable-pinch disables pinch-to-zoom functionality for touchscreens.

Depending on your use case, you may find some or all of these additional flags useful for chromium kiosk apps.

Handling power saving and blank screen

If left in default configuration, the xorg display manager will turn off the screen after some time of inactivity to save power. This may me desirable for interactive devices that have a keyboard or touchscreen attached, as any form of input will wake up the system again. For passive devices like screens displaying advertisements or informational displays, this behavior is problematic and needs to be prevented.

You can disable these features entirely by combining three xset commands. Simply prepend these lines to the beginning of /home/kioskuser/.xinitrc:

xset s off
xset -dpms
xset s noblank

The first line prevents screen blanking, the second disables dpms (Display Power Management Signaling) and the last one prevents any screen saver activation.

Using a splash screen during boot

The default boot process is very noisy, printing numerous colored lines of logging output to the screen. For many devices running kiosk mode, this boot sequence may concern unsuspecting users, so hiding most of the noise is a good idea.

This can be done by using a plymouth splash screen theme, which first needs to be installed:

sudo apt install -y plymouth plymouth-themes plymouth-x11

Plymouth comes packaged with several themes by default, you can get a list of them with

sudo plymouth-set-default-theme -l

Once you settled on a theme you want, apply it to your environment with

sudo plymouth-set-default-theme -R tribar

The tribar theme is a good default choice here, as it is lightweight enough to load fast but also clean and unintrusive.

The second part to adjust for a cleaner boot experience is the grub configuration. Open /etc/default/grub and set GRUB_TIMEOUT=0 (create it if missing). Next, find the line GRUB_CMDLINE_LINUX_DEFAULT and prepend quiet loglevel=0 splash to it.

For example, if your grub file looked like this initially:

GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT="net.ifnames=0 biosdevname=0"
GRUB_CMDLINE_LINUX="consoleblank=0"

it should look like this after modification:

GRUB_DEFAULT=0
GRUB_TIMEOUT=0
GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian`
GRUB_CMDLINE_LINUX_DEFAULT=" quiet loglevel=0 splash net.ifnames=0 biosdevname=0"
GRUB_CMDLINE_LINUX="consoleblank=0"

Finally, run

sudo upgrade-grub

to save your new configuration. The next reboot should be mostly quiet, with a splash screen or animation covering the remaining boot logging noise.

If you want even more control over the boot animation, you could design your own plymouth theme or show a static image.

Configuring virtual terminal

It is common for linux systems to dynamically provide several virtual terminals after boot. A virtual terminal is essentially a login session, just like the one we used to automatically log in. At any point in time, you may press ctrl+alt+f2 to switch to tty2 (the second virtual terminal), up to ctrl+alt+f6 for the last one.

If you are following along with virtualbox, you need to use the software keyboard from the top menu under input > Keyboard > Soft Keyboard ... to send key combinations to the vm.

Keeping this functionality may or may not be desirable: If end users can send inputs (e.g. a keyboard is connected to the device), they can effectively "disable" the device by switching to a different virtual console (where the kiosk app isn't visible, and just a text-based login appears). Only users familiar with linux systems will recognize this and be able to amend it, while less experienced people have no choice but to restart the device.

On the other hand, devices that cannot be interacted with directly like informational displays may want to keep this feature enabled to allow operators to quickly debug the device in-place, simply by attaching a keyboard to it and switching to another tty to log in and issue commands.

If you want to disable virtual terminals and leave only the session used for the kiosk mode app, you need to find these lines in /etc/systemd/logind.conf and make sure they are not commented out and have the correct values:

NAutoVTs=1
ReserveVT=0

The first line defines how many total virtual terminals are automatically generated, the second defines a reserved login virtual terminal that is always available (where value 0 disables this feature).

Your changes will take effect after the next reboot.

Preventing users from escaping the kiosk app

Simply starting an app in fullscreen may initially look like everything works as intended, but as soon as users have access to input, they may be able to break out of the kiosk app. This can take many forms, for example alt+f4 to close the kiosk app, ctrl+tab to try and switch to a different program or application-specific shortcuts like ctrl+w to close the current tab in chromium (thus closing the browser, since only one tab was open).

The only reliable way to prevent these issues is to unbind all ctrl and alt keys (left and right), as this leaves their key combinations inaccessible. You can unbind them using xmodmap when kioskuser starts their xorg session by prepending this command to the beginning of /home/kioskuser/.xinitrc:

xmodmap -e "keycode 37 = NoSymbol" \
        -e "keycode 64 = NoSymbol" \
        -e "keycode 105 = NoSymbol" \
        -e "keycode 108 = NoSymbol"

This solution has the added benefit that it only affects the kiosk app session on tty1, other sessions (e.g. over SSH) and other users remain unaffected.

After a reboot, feel free to try all key combinations to ensure you cannot escape the kiosk environment with them anymore.

If you are following along with virtualbox, you need to use the software keyboard from the top menu under input > Keyboard > Soft Keyboard ... to send key combinations to the vm.

Considerations for touchscreen devices

Before adding touchscreen features to this setup, take a moment to consider if debian is the right platform for your needs. Handheld-optimized distributions like android will be significantly easier to set up for touch devices, and you are less likely to find edge-case issues or hardware incompatibilities with them.

The primary need of a touchscreen device is a soft keyboard (an on-screen software keyboard) to allow users to input keystrokes without a physical keyboard connected. There are many options here, from lightweight choices like matchbox-keyboard (a good choice for the matchbox-window-manager) to more complex and feature-rich alternatives like florence or onboard. The choice is mainly up to your device's needs and personal preference, but be warned that setting up dynamic focus (automatically show/hide the keyboard when text input is selected) may be tricky on some combinations.

Since most touch devices do not show a cursor to the user, you likely want to hide the one on your touch device as well. An easy solution is to use unclutter with an idle timeout of 0 seconds, to hide the cursor most of the time. This setup may cause a flickering cursor on mouse move for some touchscreens, in which case you need to use a more complex solution like creating and installing a cursor theme with an invisible cursor icon.

Lastly, your kiosk application may behave unexpectedly with touch devices. The chromium browser for example, includes support for some touch gestures, like navigating with swiping motions or zooming with a pinch gesture. These can be easily disabled with flags --overscroll-history-navigation=0 and --disable-pinch, respectively. Spend some time checking if and what touch gestures your application supports, and consider how that may negatively impact the user experience.

Device maintenance and security

After setting up your device, you may think about how to maintain it in the future. Running an SSH server on the device to remotely manage it may seem like a good idea at first, but be careful to either disable SSH login for the kioskuser account or limit logins to key-only authentication, as otherwise you provide remote access to anything in the device's network through the passwordless kiosk application user.

On the topic of security, you should make it a point to treat all your deployed/installed devices as hostile. Even if no remote access is possible over SSH, an attacker could simply plug the storage disk into one of their machines and freely read/change any files on the system. Do not store credentials on the device (like S3 storage bucket logins or FTP credentials), not even hardcoded into a compiled binary (strings can be stripped out of executables easily with the strings command or debugging software).

If your device needs authenticated access to remote infrastructure, for example to forward orders or reports, consider deploying a purpose-built REST API with limited access for the devices, and provide different API tokens for each device to easily lock specific ones out of the service should they start behaving undesirably or their token becomes compromised. Audit logging and rigid monitoring/alerting are a valuable addition of such an API in production environments.

More articles

How to use ansible with vagrant environments

Painlessly connect vagrant infrastructure and ansible playbooks

Why boring software is a smart choice

Not everything is about excitement

Common pitfalls running docker in production

Avoiding the mistakes many make when embracing containerized deployments

The downsides of source-available software licenses

And how it differs from real open-source licenses