New kiosk miniPC

With a ~3 secs boot and minimal Alpine-Linux setup

Posted by Giulio Magnifico on Thursday, April 24, 2025

I got a new little toy: a miniPC for €189 that serves as a kiosk, instead of the Raspberry PI 4B, which had become a bit too slow for large Grafana dashboards (especially for scrolling on the touch display).

At first, I thought about getting a Raspberry Pi 5, but after doing some quick math, I realized I would have spent about the same for a less powerful device:

  • Raspberry PI 5 8GB: ~95€
  • Heat sink with M.2 slot: ~30€
  • SSD NVMe: ~30€
  • Power supply: ~20€
  • Total: 170-180€

But at the same price I bought a miniPC with an Intel Alder Lake N150 CPU, 16GB of RAM, and a 512 SATA SSD. This one: Acemagic V1

All this mini PC needs to do is display a page with a Grafana dashboard that update every minute, start up as fast as possible, and be smooth when scrolling through the pages with the touchscreen. And here it is:

MiniPC

And I think I managed to achieve everything pretty well, with a boot time of ~3.5 seconds:

kiosk:~$ cat /var/log/boot-time.log
Booted in  3.2 seconds
Mon Apr 28 05:11:20 CEST 2025

Here’s also quick a video of the boot and “functionality”:

MiniPC

To my surprise (P.S. It was 25 years since I last bought a non-Apple computer), it’s excellent: it’s well-built, doesn’t heat up, and the internal layout is well designed:

MiniPC opened

The first thing I did was change the thermal paste to a better one to ensure the fan turns on as infrequently as possible:

MiniPC after MiniPC cleaned

Then I replaced the internal Sata M2 drive with an NVMe one for 27€ (this 256Gb SanDisk), going from about 375MBps to ~850MBps:.

kiosk:~$ doas hdparm -t /dev/nvme0n1
doas (kiosk@kiosk) password:

/dev/nvme0n1:
 Timing buffered disk reads: 2530 MB in  3.00 seconds = 843.07 MB/sec

I also installed an old 3200MHz RAM stick, but it doesn’t make any noticeable difference:

MiniPC RAM SSD
⚠️
About the SSD… At first, it was only running at Gen2 x1 (around 450MBps), so I went into the hidden BIOS (Ctrl + F1) and disabled a bunch of options on the SSD’s root port. I also tried to free up more PCIe lanes without success. I thought they might be shared with Wi-Fi or USB, so I disabled those too, but the bandwidth still stayed at Gen3 x1
MiniPC bios

Anyway, the speed more than doubled, which is a nice improvement.

Software

My first idea was to install Gentoo starting from stage 0 or 1 (it’s the distro I used 20 years ago), but I quickly realized that nowadays it’s officially supported only for developers. I would have had to install it from Stage 3 and then recompile everything… way too long and complicated.

So I started looking for another distribution that would better fit my needs (it had been at least 20 years since I last used Linux on an x86 computer, and a lot has changed).

In the end, I went with the “Standard” version of Alpine Linux. The installation was easy and took just a few minutes, the next step was setting up a minimal graphical environment —Wayland and Sway— to launch Firefox in kiosk mode with the Grafana dashboard.

The file everything starts from is an OpenRC service: /etc/init.d/kiosk

kiosk:~$ cat /etc/init.d/sway-kiosk
#!/sbin/openrc-run

description="Sway Kiosk Launcher"

command="/bin/su"
command_args="- kiosk -c 'exec sway'"
command_background=true

And the Sway config file that launches Firefox, sets up touchscreen input, and starts the VNC server:

# Theme
bar {
    position top
    status_command while date +'%Y-%m-%d %X'; do sleep 1; done

    colors {
        statusline #ffffff
        background #323232
        inactive_workspace #32323200 #32323200 #5c5c5c
    }
}

include /etc/sway/config.d/*

# Vertical display
output HDMI-A-1 transform 90

# TouchScreen
input "10176:2137:wch.cn_TouchScreen" {
    map_to_output HDMI-A-1
}

# Firefox kiosk
exec MOZ_ENABLE_WAYLAND=1 MOZ_USE_XINPUT2=1 firefox --kiosk --no-remote --profile /home/kiosk/.mozilla/firefox/kiosk "http://192.168.1.6:3001/d/FGioSdO4z31m/casa-summary?orgId=1&refresh=2m&from=now-12h&to=now&timezone=browser&kiosk"

# VNC server (senza password)
exec wayvnc 0.0.0.0 5900

# VNC Clipboard sync
#exec sleep 2 && wl-paste --watch clipman store

Using this Firefox profile:

user_pref("gfx.webrender.all", true);
user_pref("gfx.webrender.enabled", true);
user_pref("layers.acceleration.force-enabled", true);
user_pref("media.ffmpeg.vaapi.enabled", true);
user_pref("media.hardware-video-decoding.force-enabled", true);
user_pref("widget.dmabuf.force-enabled", true);
user_pref("browser.sessionstore.resume_session_once", false);
user_pref("browser.sessionstore.resume_from_crash", false);
user_pref("browser.startup.page", 0); 

Lastly, I installed the usual Prometheus exporter, prometheus-node-exporter, to build the usual Grafana dash and get some useful alerts:

Grafana alerts

Optimizations

After reaching the final stage, I started looking for some optimizations to make the boot faster. Since I usually turn it off when I leave home (to avoid unnecessary heat inside the cabinet and wasting energy), I wanted to speed up the boot when turning it back on.

If I’m away for just a short time, instead of shutting it down completely, it automatically turns off the display using a HomeKit geofence (and turns it back on when I get close to home).

Starting with the GRUB boot options:

GRUB_CMDLINE_LINUX_DEFAULT="root=UUID=1234 modules=sd-mod,USB-storage,ext4,NVMe quiet splash loglevel=0 fastboot"

To the Init configuration of BusyBox:

# /etc/inittab
::sysinit:/sbin/openrc sysinit
::sysinit:/sbin/openrc boot
::wait:/sbin/openrc default
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/openrc shutdown

Then Init call OpenRC using the /etc/rc.conf where I stripped out all the comments and unnecessary text and the final config looks like this:

rc_parallel="YES"
rc_logger="NO"
rc_verbose="NO"
rc_silent_boot="YES"
rc_depend_strict="NO"
rc_tty_number=1

Moreover, from the boot services I have enabled these services

Runlevel: default
 dbus                        [  started  ]
 dropbear                    [  started  ]
 prometheus-node-exporter    [  started  ]
 udev-postmount              [  started  ]
 crond                       [  started  ]
 seatd                       [  started  ]
 acpid                       [  started  ]
 local                       [  started  ]

Runlevel: shutdown
 savecache                   [  stopped  ]
 killprocs                   [  stopped  ]
 mount-ro                    [  stopped  ]

Runlevel: boot
 modules                     [  started  ]
 hwclock                     [  started  ]
 hostname                    [  started  ]
 swap                        [  started  ]
 sysctl                      [  started  ]
 bootmisc                    [  started  ]
 syslog                      [  started  ]
 loadkmap                    [  started  ]
 networking                  [  started  ]
 seedrng                     [  started  ]

Runlevel: nonetwork

Runlevel: sysinit
 devfs                       [  started  ]
 udev                        [  started  ]
 dmesg                       [  started  ]
 udev-trigger                [  started  ]
 udev-settle                 [  started  ]

Dynamic Runlevel: hotplugged

Dynamic Runlevel: needed/wanted
 sysfs                       [  started  ]
 fsck                        [  started  ]
 root                        [  started  ]
 localmount                  [  started  ]

Dynamic Runlevel: manual

The main differences with a stock minimal Alpine Linux are:

  • Removed elogind
  • Removed chronyd (it was the slowest process
  • Switched from OpenSSH to Dropbear for SSH access

I also removed the loading of unnecessary kernel modules by deleting them before regenerating the initramfs (this hardware is also disabled in the BIOS):

rm -f /lib/modules/*/kernel/drivers/net/wireless/realtek/rtw88/*
rm -f /lib/modules/*/kernel/net/mac80211/mac80211.ko*
rm -f /lib/modules/*/kernel/net/wireless/cfg80211.ko*
rm -f /lib/modules/*/kernel/net/rfkill/rfkill.ko*
rm -f /lib/modules/*/kernel/lib/libarc4.ko*
rm -f /lib/modules/*/kernel/drivers/bluetooth/*

Lastly, I use the script boot-time.start saved in /etc/local.d/ to retrieve the boot time. It shows the duration from system startup to the login prompt or the end of OpenRC services loading:

cut -d' ' -f1 /proc/uptime | awk '{printf "Booted in  %.1f seconds\n", $1}' > /var/log/boot-time.log
date >> /var/log/boot-time.log

Shortcut automations

To automate turning the kiosk on and off, I used the Shortcuts I had already set up for the Raspberry Pi, see this post: Automate a RPi kiosk | Giulio Magnifico

Of course, I had to tweak the commands to match the new hardware setup instead of the Raspberry Pi:

Shortcuts

So now the scripts have become:

#!/bin/sh

uid=$(id -u kiosk)
export XDG_RUNTIME_DIR=/run/user/$uid
export SWAYSOCK=$(find "$XDG_RUNTIME_DIR" -type s -name 'sway-ipc.*.sock' 2>/dev/null | head -n 1)

[ -n "$SWAYSOCK" ] && swaymsg output HDMI-A-1 enable

HDMI-A-1 enable turns on the display and identical script with HDMI-A-1 disable turns it off.

Consumption

I thought this would be the worst part, since this mini PC obviously doesn’t have an ARM architecture, but I was surprised: it doesn’t consume much more than a Raspberry Pi 5. Normally just around 6–7W:

MiniPC energy

Conclusions

I’m pretty happy with this small purchase because, with a “modest expense” of around €200–250, I got an excellent kiosk setup and also had the chance to learn more about Alpine Linux.

The only real tricky part is figuring out (or guessing) the right NVMe SSD, in case you want to use one, because many aren’t compatible and others start but then Alpine Linux can’t write on them due to controller errors. I tried 3 different SSDs and found that this SanDisk Plus 250GB for €25 works perfectly… well, after spending two nights finding the right BIOS settings 😴 Another SSD that works immediately at boot is the Kingston KC3000, but it costs €65 and it’s kind of wasted when only one PCIe lane is active.