Show only a splash screen on an embedded Linux kiosk boot

7 min read · Published November 4, 2026

Four changes give a clean kiosk boot: append quiet and fbcon=map:1 to the kernel arguments so no text draws on the panel, add your app-launch service to the After= line of plymouth-quit.service so the splash holds, free plymouth-quit-wait.service so nothing deadlocks, and set shell=kiosk-shell.so in weston.ini. The screen then shows the splash, then the application, and nothing else.

A clean kiosk boot is four ordinary configuration changes, not a rewrite. Keep the kernel text off the panel with two arguments set from the bootloader. Hold the splash by pointing plymouth-quit.service at your app-launch service. Free plymouth-quit-wait.service so the chain cannot deadlock. Replace the desktop shell in weston.ini with the kiosk shell. On the board where this chain was debugged, the kernel loaded in about 3 s, so the first seconds stay dark, with two or three backlight blinks, before the splash takes the panel. From there it goes splash, then application, with no background and no top bar.

What controls what the HDMI panel shows during boot?

Four layers decide it. The kernel command line prints or suppresses text. The plymouth units own the splash. Systemd ordering decides when the splash ends. Weston.ini decides what the desktop draws around your application once it starts. Fix this map in your head first. Every symptom you will meet belongs to exactly one of these layers, and the fix is always the setting that owns it.

What you see Layer that draws it Setting that changes it
Scrolling kernel text kernel console quiet and fbcon=map:1 in the kernel arguments
Splash that disappears too early plymouth After= line of plymouth-quit.service
Splash that never exits systemd wait chain plymouth-quit-wait.service dependencies
Desktop background and top bar weston shell shell=kiosk-shell.so in weston.ini

How do I stop the boot text appearing on the HDMI output?

Append fbcon=map:1 and quiet to the kernel command line from the bootloader. On this build it worked as fw_setenv setupargs "fbcon=map:1 quiet". The console then stops drawing on the panel and the kernel stops printing routine messages. The variable name depends on your build: it was setupargs here, it is bootargs on many boards, and one vendor image uses a dedicated extra-arguments variable.

  1. Run cat /proc/cmdline and read what is on the command line today, so you know which variable your bootloader concatenates into it.
  2. Set both arguments in one go, for example fw_setenv setupargs "fbcon=map:1 quiet", using your board's variable name.
  3. Reboot and watch the panel. The text is gone. The first 3 s or so stay dark, and the backlight blinked on and off two or three times during handoff on the board this was debugged on. Treat the blinks as cosmetic.

These two arguments are the standard answer for disabling boot and kernel log output on an HDMI panel on embedded boards. If the panel stays black after the arguments land, the fault is in the video chain, not the splash: work through troubleshooting an embedded controller video display.

How do I hold the splash until my application starts?

Add your app-launch service to the After= line of plymouth-quit.service. Then remove the waiting dependency on plymouth-quit-wait.service from weston.service and [email protected], so the splash quits one step before the application draws. Out of the box, the splash quits early: on the image in question the unit's line read After=rc-local.service plymouth-start.service systemd-user-sessions.service, plus whatever the image adds, which left black gaps and text flashes between the splash and the desktop.

  1. Edit plymouth-quit.service and append your launcher: After=rc-local.service plymouth-start.service systemd-user-sessions.service wayland-app-launch.service. Substitute the name of your own app-launch unit.
  2. Open weston.service and [email protected] and delete plymouth-quit-wait.service from their After= lines. Step 2 is what makes step 1 safe.
  3. Run systemctl daemon-reload so systemd sees the edited units, then reboot.
  4. The splash now covers everything up to the moment the application starts, with no in-between state on the panel.

A reference OS for boards of this class ships exactly this pattern as a patch, delaying plymouth-quit by a wide margin to eliminate the in-between states. Carrying your two unit edits as a patch in the image build matches that practice and survives a reflash.

Why does the splash freeze when I add weston.service to that line?

Because it creates a circular wait. plymouth-quit.service waits for weston.service. weston.service and [email protected] wait for plymouth-quit-wait.service. That unit waits for plymouth to quit. Nothing moves. The one-line edit looks right and hangs the boot with the splash frozen on screen.

The tell is systemctl list-jobs run during boot, from serial or over the network: it shows waiting jobs chained through plymouth-quit-wait.service. The fix is step 2 in the previous section, taking the wait off the downstream services and leaving your dependency where it belongs, on plymouth-quit itself. On the board this was debugged on, that one removal turned a frozen splash into a splash that quits right before the application.

How do I drop the desktop background and the top bar?

Set shell=kiosk-shell.so under the [core] section of /etc/xdg/weston/weston.ini, then restart weston. The desktop shell is replaced by a kiosk shell in which your application is the only surface on the panel. The two lines are the whole change:

  • [core]
  • shell=kiosk-shell.so

The same selection can be made at launch with --shell=kiosk-shell.so on the weston command line, which suits a service that starts the compositor by hand. Version differences matter here, and community notes on getting a kiosk shell running on older weston releases cover the details for that generation. On the bench result that matters to a kiosk: background gone, upper bar gone, application full-screen.

How do I confirm the boot order actually changed?

Render the boot chain with systemd-analyze plot > chain.svg, open the SVG in a browser, and check that plymouth-quit.service now starts after your app-launch service. Use systemctl list-jobs during boot to catch a stuck chain in the act. Three checks, in this order:

  1. systemd-analyze plot > chain.svg. The plymouth-quit bar must begin to the right of your app-launch bar.
  2. During boot, from serial or network, systemctl list-jobs. Waiting jobs chained on plymouth-quit-wait.service mean the circular wait is back.
  3. cat /proc/cmdline. Both quiet and fbcon=map:1 must be present, or the bootloader variable was the wrong one.

One rule while you work: keep that serial or network session open whenever you change boot arguments. A mistyped value in the environment can stop the board short of the application, and that session is the way back in.

Can the splash start before the kernel finishes loading?

Only from the initramfs. A splash built into the initramfs starts while the root filesystem is still being mounted. A splash started from the root filesystem can never appear before the kernel hands control over, about 3 s after power-on on the board in question. The 3 s figure is a measured kernel load time, read off systemd-analyze, on that specific build, so treat it as the order of magnitude for a small ARM board and measure your own.

The documented route is the initramfs itself: include plymouth's early hook in the initramfs image in your build so the splash starts at the earliest possible point. The exact recipe depends on your BSP layer. Before that point, the bootloader is the only code running, and a bootloader logo is the only pixel it can put up.

How long does each stage take in an example kiosk boot?

Take the board's measured 3 s kernel load. Suppose systemd-analyze shows weston.service begin at the 6 s mark and wayland-app-launch.service at 8 s. The arithmetic gives 5 s of splash between a 3 s dark start and the application at 8 s. Laid out stage by stage:

  • 0 to 3 s: dark. The kernel loads; with the two arguments set, nothing draws.
  • 3 s: the kernel hands over and plymouth-start.service puts the splash up.
  • 3 to 8 s: the splash holds, because plymouth-quit waits for wayland-app-launch.service. That is 8 minus 3, so 5 s of splash carrying your logo.
  • 8 s: the splash quits and the application draws full-screen under the kiosk shell.

Total power-on to application: 8 s, of which 5 s are your splash. Read your own two timestamps off chain.svg and run the same subtraction; the method transfers to any board.

When should the screen be a signage display instead of a kiosk build?

When its whole job is playing prepared media, an offline Android signage display does this out of the box: it starts into playback from the card in the slot and needs none of the boot-chain work above. Our displays play images and video from a microSD card or USB stick, with the player software included and no subscription, so setting up offline playlist autostart is a settings job, not a build job.

They run their own player, so a Linux application kiosk is not their work, and their HDMI port is an output. For a notice board, a menu loop or a small status screen fed from a card, choosing an offline signage kiosk display for an operator view is the shorter path, the guide to andon boards says where such a screen fits on a floor, and the small display range covers the sizes.

Recommendation: if this panel runs your own application, make the four changes above and carry them as patches in the image build so a reflash cannot undo them; if the panel's whole job is playing prepared media, fit an offline signage display and skip the boot chain entirely.

Questions

Can I still get a login prompt while the HDMI shows no boot text?

Yes. Keep a serial console enabled in the kernel arguments and work over the UART. The boot fix matters here too: [email protected] normally waits on plymouth-quit-wait.service, so leave that dependency removed or the serial login hangs behind a splash that never quits.

Does the quiet argument throw away the kernel messages?

No. quiet lowers what the kernel prints to the console during boot; the messages still land in the kernel ring buffer. Once the application is up, dmesg or journalctl -k shows the full boot log, so you lose nothing for debugging a slow start.

Should I edit these files on the running board or rebuild the image?

Edit on the board to find the values that work; that is the fast loop, with systemctl daemon-reload after each unit change. For production, carry the kernel arguments, the two plymouth unit edits and weston.ini as patches in the image build, because a reflash reverts every on-target edit.

Why are the first seconds still dark, and can anything cover them?

Something has to bring the panel up before any pixels can be drawn. On the board this chain was debugged on, the kernel loaded in about 3 s and a splash started from the root filesystem cannot appear before that handover. Building plymouth into the initramfs moves the splash earlier; only the bootloader can put pixels up sooner, with a logo of its own.

Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.

Keep reading

Build your signAsk for a quote