Hey everyone,
I wanted to share how I got a fullscreen touchscreen kiosk running on my ZimaBoard 2 connected to a 22" touch monitor via HDMI.
Since ZimaOS has no package manager (apt doesn’t exist), the trick is to do everything inside Docker containers.
My Setup
-
ZimaBoard 2 1664 (Intel N100, 16 GB RAM)
-
ZimaOS v1.5.4
-
22" touch monitor via HDMI + USB-C→USB-A touchscreen input
The Approach
ZimaOS is Buildroot-based — no apt, no Xorg, no desktop environment. Instead, we run a Debian container with Xorg modesetting driver + Chromium in kiosk mode, with /dev/dri passed through. A separate nginx:alpine container serves the dashboard files.
Step 1 — Add your user to the docker group
Open the web terminal at http://your-zima-ip:7681 and run:
bash
sudo usermod -aG docker YOUR_USERNAME
Then reconnect SSH for the change to take effect.
Step 2 — Create the files
bash
mkdir -p /DATA/AppData/kiosk/dashboard
/DATA/AppData/kiosk/Dockerfile:
text
FROM debian:bookworm-slim
RUN echo 'deb http://deb.debian.org/debian bookworm-backports main' >> /etc/apt/sources.list
RUN apt-get update && \
apt-get install -y \
xserver-xorg-core \
xserver-xorg-input-libinput \
openbox chromium \
x11-xserver-utils xinit \
fonts-noto-core && \
apt-get install -y -t bookworm-backports libgl1-mesa-dri && \
rm -rf /var/lib/apt/lists/*
COPY xorg.conf /etc/X11/xorg.conf
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
Why backports Mesa? The Intel N100 (Alder Lake-N, PCI ID 0x46d4) is only supported from Mesa 23.0+. Debian bookworm ships 22.3. Backports provides 25.x.
/DATA/AppData/kiosk/xorg.conf:
text
**Section "Device"
Identifier "Intel"
Driver "modesetting"
Option "AccelMethod" "none"
EndSection
Section "Screen"
Identifier "Default"
Device "Intel"
EndSection
Section "InputDevice"
Identifier "Touchscreen"
Driver "libinput"
Option "Device" "/dev/input/event8"
EndSection
Section "ServerLayout"
Identifier "Default"
Screen "Default"
InputDevice "Touchscreen" "CorePointer"
EndSection**
Find your touch device event number with:
bash
cat /proc/bus/input/devices | grep -A3 -i touch
/DATA/AppData/kiosk/entrypoint.sh:
text
#!/bin/sh
Xorg :0 vt1 -s 0 -dpms -nolisten tcp &
sleep 4
export DISPLAY=:0
xset s off && xset -dpms && xset s noblank
openbox --sm-disable &
sleep 1
exec chromium \
--kiosk --no-sandbox --disable-gpu \
--disable-dev-shm-usage \
--touch-events=enabled \
--no-first-run --disable-infobars \
"${KIOSK_URL:-http://localhost:8888}"
/DATA/AppData/kiosk/nginx.conf:
text
server {
listen 8888;
root /usr/share/nginx/html;
index index.html;
}
Put your dashboard HTML/CSS/JS files into /DATA/AppData/kiosk/dashboard/.
Step 3 — Build and Run
Build (note: docker build needs --config /tmp on ZimaOS):
bash
docker --config /tmp build -t kiosk:latest /DATA/AppData/kiosk
Dashboard web server:
bash
docker --config /tmp run -d \
--name dashboard-server \
--restart unless-stopped \
--network host \
-v /DATA/AppData/kiosk/dashboard:/usr/share/nginx/html:ro \
-v /DATA/AppData/kiosk/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
nginx:alpine
Kiosk display:
bash
docker --config /tmp run -d \
--name jarvis-kiosk \
--restart unless-stopped \
--privileged \
--network host \
-e KIOSK_URL=http://localhost:8888 \
-v /DATA/AppData/kiosk/xorg.conf:/etc/X11/xorg.conf:ro \
-v /DATA/AppData/kiosk/entrypoint.sh:/entrypoint.sh:ro \
-v /run/udev:/run/udev:ro \
kiosk:latest
Both containers have --restart unless-stopped so they survive reboots automatically.
Touchscreen Click Fix (important!)
Xorg’s libinput maps touch as pointer (mouse) events. Chromium then applies a tiny movement threshold for click — even a 1px finger shift prevents it from firing. If your dashboard uses onclick handlers, add this JS to replace them with pointerup + a 20px threshold:
js
(function touchFix() {
const downs = {};
document.addEventListener('pointerdown', e => {
downs[e.pointerId] = { x: e.clientX, y: e.clientY };
}, { passive: true });
function isTap(e) {
const d = downs[e.pointerId];
return !d || (Math.abs(e.clientX - d.x) < 20 && Math.abs(e.clientY - d.y) < 20);
}
function run(fn) { try { new Function(fn)(); } catch(ex) {} }
function patchButtons(root) {
root.querySelectorAll('button[onclick]').forEach(btn => {
const oc = btn.getAttribute('onclick');
btn.removeAttribute('onclick');
btn.addEventListener('pointerup', e => { e.stopPropagation(); if (isTap(e)) run(oc); });
});
}
window.addEventListener('load', () => {
document.querySelectorAll('.widget[onclick]').forEach(el => {
const oc = el.getAttribute('onclick');
el.removeAttribute('onclick');
el.addEventListener('pointerup', e => { if (isTap(e) && !e.target.closest('button')) run(oc); });
});
patchButtons(document.body);
});
new MutationObserver(muts => muts.forEach(m =>
m.addedNodes.forEach(n => { if (n.nodeType === 1) patchButtons(n); })
)).observe(document.body, { childList: true, subtree: true });
})();
Also add to your CSS to hide the mouse cursor on a touchscreen:
css
* { cursor: none !important; touch-action: manipulation; }
Troubleshooting
| Problem | Fix |
|---|---|
docker build fails with unknown flag |
Use docker --config /tmp build … |
| Black screen / Basic output test failed | Use Xorg modesetting, not Wayland/cage |
| Mesa doesn’t support N100 | Install libgl1-mesa-dri from bookworm-backports |
| Touch input not detected | Check event number with /proc/bus/input/devices, update xorg.conf |
| Dashboard shows ZimaOS login | Port 80 is taken by ZimaOS — use a different port (8888 works) |
Hope this helps someone!



