How to setup Chromium Kiosk on Ubuntu 26.04 in 30 seconds

This script turns a fresh Ubuntu 26.04 Server installation into a locked-down Chromium kiosk that boots straight into a full-screen browser pointed at the URL of your choice. Save it as setup-kiosk.sh, run it once as root, and the machine will reboot into kiosk mode.

The script

setup-kiosk.sh
#!/usr/bin/env bash
set -euo pipefail

# Target URL (Defaults to ubuntu.com if not supplied as argument $1)
KIOSK_URL="${1:-https://ubuntu.com}"
KIOSK_USER="kiosk"

echo "========================================="
echo " Starting Ubuntu 26.04 Server Kiosk Setup"
echo " Target URL: ${KIOSK_URL}"
echo "========================================="

# 1. Precondition Check: Root Privileges
if [[ $EUID -ne 0 ]]; then
    echo "[!] Error: This script must be executed with root privileges." >&2
    echo "    Usage: sudo ./setup-kiosk.sh [OPTIONAL_URL]" >&2
    exit 1
fi

# 2. Precondition Check: Network Connection
echo "[+] Checking network connectivity..."
if ! ping -c 1 -W 3 8.8.8.8 &>/dev/null; then
    echo "[!] Error: No active network connection detected." >&2
    echo "    Please verify IP configuration or cabling before proceeding." >&2
    exit 1
fi

# 3. Install Minimal Display, Desktop Helpers & Browser Dependencies
echo "[+] Updating apt repositories and installing required dependencies..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq \
    xserver-xorg \
    xserver-xorg-video-all \
    xinit \
    x11-xserver-utils \
    openbox \
    unclutter \
    chromium-browser \
    xdg-utils \
    dbus-x11 \
    desktop-file-utils

# 4. Create Dedicated Restricted Kiosk User
if ! id "$KIOSK_USER" &>/dev/null; then
    echo "[+] Creating non-root user '$KIOSK_USER'..."
    useradd -m -s /bin/bash "$KIOSK_USER"
fi

# Grant user permission for direct display, rendering, input, and audio hardware
usermod -aG video,audio,input,render "$KIOSK_USER"

# 5. Fix Xorg Execution Permissions for Non-Root Service Startup
echo "[+] Configuring X11 wrapper permissions..."
mkdir -p /etc/X11
cat <<'EOF' > /etc/X11/Xwrapper.config
allowed_users=anybody
needs_root_rights=yes
EOF

# 6. Configure Openbox Environment and Chromium Launcher
echo "[+] Setting up autostart profile for '$KIOSK_USER'..."
KIOSK_HOME=$(getent passwd "$KIOSK_USER" | cut -d: -f6)
AUTOSTART_DIR="$KIOSK_HOME/.config/openbox"
mkdir -p "$AUTOSTART_DIR"

cat <<EOF > "$AUTOSTART_DIR/autostart"
#!/usr/bin/env bash

# Disable DPMS and display power-saving / sleep
xset s off
xset s noblank
xset -dpms

# Hide mouse cursor on idle
unclutter -idle 0.5 -root &

# Ensure an active D-Bus session for Chromium snap confinement
if [ -z "\${DBUS_SESSION_BUS_ADDRESS:-}" ]; then
    eval \$(dbus-launch --sh-syntax --exit-with-session)
fi

# Infinite execution loop for Chromium kiosk resilience
while true; do
  chromium \
    --kiosk \
    --noerrdialogs \
    --disable-infobars \
    --no-first-run \
    --disable-session-crashed-bubble \
    --disable-component-update \
    --check-for-update-interval=31536000 \
    "$KIOSK_URL"
  sleep 1
done
EOF

chmod +x "$AUTOSTART_DIR/autostart"
chown -R "$KIOSK_USER:$KIOSK_USER" "$KIOSK_HOME/.config"

# 7. Create and Enable Systemd Kiosk Service with Snap Cgroup Delegation
echo "[+] Registering systemd kiosk service..."
cat <<EOF > /etc/systemd/system/kiosk.service
[Unit]
Description=Chromium Kiosk Service
After=network-online.target sound.target dbus.service
Wants=network-online.target dbus.service

[Service]
User=$KIOSK_USER
Group=$KIOSK_USER
Type=simple
Delegate=yes
PAMName=login
ExecStart=/usr/bin/startx /usr/bin/openbox-session -- :0 -nolisten tcp -nocursor
Restart=always
RestartSec=3
Environment=DISPLAY=:0

[Install]
WantedBy=graphical.target
EOF

# Set default target to graphical and enable kiosk auto-start
systemctl set-default graphical.target
systemctl daemon-reload
systemctl enable kiosk.service

echo "========================================="
echo " Kiosk setup complete!"
echo " The system will reboot into kiosk mode."
echo "========================================="
sleep 3
reboot

How to change the URL

The kiosk URL is controlled by a single variable near the top of the script:

setup-kiosk.sh
KIOSK_URL="${1:-https://ubuntu.com}"

You have two options:

  1. Pass it on the command line when you run the installer — this is the recommended way because it leaves the script untouched:

    example.sh
    sudo ./setup-kiosk.sh https://your-dashboard.example.com
  2. Edit the default in the script itself if you want a permanent fallback URL. Change https://ubuntu.com to your target URL. This value is only used when no argument is supplied.

Note that the URL is baked into the autostart file (~kiosk/.config/openbox/autostart) during setup. If you want to change the URL after the kiosk is already installed, edit the chromium ... "$KIOSK_URL" line inside that file and restart the service:

example.sh
sudo systemctl restart kiosk.service

How it works, step by step

1. Root check

$EUID is the effective user ID. The script refuses to run as a non-root user because it needs to install packages, create a system user, write to /etc/X11, and register a systemd unit.

2. Network check

A single ICMP packet to 8.8.8.8 with a 3-second timeout. If the box has no network, apt-get would fail halfway through and leave the system in a broken state, so we bail out early with a clear message.

3. Package installation

DEBIAN_FRONTEND=noninteractive plus -qq keeps apt quiet and stops it from prompting for things like keyboard configuration. The packages installed are the absolute minimum needed to drive a display and run a browser:

  • xserver-xorg, xserver-xorg-video-all, xinit — the X server itself
  • x11-xserver-utils — provides xset, used later to disable screen blanking
  • openbox — a tiny, almost featureless window manager; perfect for a kiosk because there is nothing for the user to interact with
  • unclutter — hides the mouse cursor when it is not moving
  • chromium-browser — the actual kiosk application
  • xdg-utils, desktop-file-utils, dbus-x11 — helpers Chromium and the snap runtime pull in indirectly

4. Dedicated kiosk user

Chromium never runs as root. The script creates a kiosk user with no password and a home directory, then adds it to the video, audio, input, and render groups so it can talk directly to the GPU, sound card, and input devices without sudo.

5. Xwrapper config

By default, Xorg refuses to be started by a non-root user. /etc/X11/Xwrapper.config with allowed_users=anybody relaxes that restriction, and needs_root_rights=yes lets X grant itself the privileges it needs for direct hardware access. This is what allows the kiosk user to run startx from inside a systemd service.

6. Openbox autostart

Openbox runs ~/.config/openbox/autostart when it starts. The generated script does four things:

  • xset s off / xset s noblank / xset -dpms — disable the screensaver, screen blanking, and DPMS power management so the display never goes to sleep.
  • unclutter -idle 0.5 -root — hides the cursor after half a second of inactivity. In a touch-only kiosk there should be no visible cursor.
  • dbus-launch — Chromium under snap confinement needs a session D-Bus. If the service did not provide one, we start it here.
  • while true; do chromium --kiosk ...; sleep 1; done — the resilience loop. If Chromium crashes, is closed, or the X server hiccups, the loop restarts it within a second. The flags disable every interactive dialog Chromium would otherwise show on first run or after a crash (--no-first-run, --disable-session-crashed-bubble, --disable-infobars, --noerrdialogs), and --check-for-update-interval=31536000 effectively disables update checks for a year so the browser does not try to phone home.

7. systemd service

The unit file runs startx /usr/bin/openbox-session as the kiosk user on DISPLAY=:0. The interesting directives are:

  • After=network-online.target ... Wants=network-online.target — wait for the network to be up so Chromium does not boot into an offline error page.
  • Delegate=yes — lets the service manage its own cgroup sub-tree, which is required for the snap version of Chromium to set up its sandbox.
  • PAMName=login — runs the service through a PAM login session so things like dbus-launch, logind, and ACLs on device nodes work the same way they would for a real logged-in user.
  • Restart=always / RestartSec=3 — if the X server or openbox dies, systemd brings the whole kiosk back up after 3 seconds.

systemctl set-default graphical.target makes the box boot into the graphical target by default, and systemctl enable kiosk.service makes the kiosk start automatically on every boot.

Final reboot

The script ends with sleep 3; reboot so you get a clean boot into the new graphical target with the kiosk service enabled, rather than trying to start X on top of whatever state the install left the system in.

Usage

example.sh
chmod +x setup-kiosk.sh
sudo ./setup-kiosk.sh https://your-dashboard.example.com

After the reboot you should see Chromium in full-screen kiosk mode pointing at your URL. To recover a shell, switch to a virtual console with Ctrl+Alt+F3 and log in as a normal user.

Also see How to run a second program on a second screen with one X server for dual-monitor kiosk setups — and why two separate X servers don’t work on a single GPU.


Check out similar posts by category: Ubuntu Chromium