How to setup Onboard Virtual Keyboard for Debian 13 + KDE Plasma 6 + Wayland (uinput-based)

Preface: From Almost Giving Up to Finally Being Able to Type

Honestly, this article was written when I was very close to giving up on using an on-screen keyboard under KDE Plasma Wayland.

On Debian 13 with KDE Plasma 6 running on Wayland, the only officially supported virtual keyboard is Maliit.
In practice, however, it comes with a series of nearly deal-breaking problems:

  • The keyboard only appears once
  • After swiping it down, it can never be summoned again
  • Requires restarting KWin or the entire desktop session
  • Completely unsuitable for real tablet or 2-in-1 usage

After digging through GitHub issues and KDE Discuss threads, I honestly started to think:

“Maybe choosing KDE Wayland on a touch device is simply a dead end.”

That changed when I found a KDE Discuss thread from 2022:

Plasma 6 and Wayland no on-screen keyboard working - Help - KDE Discuss

In that thread, a user named @INVICTRA mentioned:

I managed to get Onboard working on wayland. Kubuntu 25.04

Edit the shortcut and add GDK_BACKEND=x11
Set input source to GTK
Set keystroke generator to uinput

The post was short and incomplete, but it revealed something important:

Onboard + X11 backend + uinput might be the real breakthrough.

With that clue, I started experimenting, filling in the missing pieces: kernel modules, permissions, udev rules, and Wayland constraints.
In the end, I successfully achieved a stable, repeatable, non-freezing virtual keyboard on:

Debian 13 + KDE Plasma 6 + Wayland

This article is the fully documented result of that process.


1. Why This Is Necessary (Background)

  • As of late 2025, KDE Plasma Wayland officially supports only Maliit
  • Maliit currently suffers from severe bugs on Plasma (cannot be re-opened after hiding)
  • Wayland intentionally forbids synthetic input (fake keyboard/mouse events)
  • Onboard can create a real input device via the Linux kernel uinput subsystem
  • uinput devices are kernel-level input devices and are not blocked by Wayland

In short:

Wayland does not allow you to fake keystrokes,
but uinput lets you attach a real virtual keyboard.

Under Debian + KDE Plasma Wayland today,
this is practically the only solution that actually works.


2. System Requirements

  • Debian GNU/Linux 13
  • KDE Plasma 6
  • Wayland session
  • XWayland installed (usually installed by default)
  • User has sudo privileges

3. Install Required Packages

sudo apt update
sudo apt install onboard xwayland

4. Enable the Kernel uinput Module

1. Check if uinput is loaded

lsmod | grep uinput

If there is no output, load it manually:

sudo modprobe uinput

2. Enable uinput at boot

echo uinput | sudo tee /etc/modules-load.d/uinput.conf

5. Configure uinput Permissions (Critical Step)

1. Create the group

sudo groupadd -f uinput

2. Add your user to the group (example: hln)

sudo usermod -aG uinput hln

Important: You must log out or reboot after this step.


3. Create a udev rule

sudo nano /etc/udev/rules.d/99-uinput.rules

Contents:

KERNEL=="uinput", MODE="0660", GROUP="uinput"

Reload rules:

sudo udevadm control --reload
sudo udevadm trigger

4. Verify After Reboot

ls -l /dev/uinput

Expected output:

crw-rw---- 1 root uinput /dev/uinput

Confirm group membership:

groups

You should see uinput.


6. Launch Onboard Using the X11 Backend (Very Important)

Under Wayland, Onboard must be forced to use the X11 backend:

GDK_BACKEND=x11 onboard

It is recommended to test this first in a terminal.


7. Required Onboard Settings

Open Onboard → Preferences → Keyboard → Advanced

Set the following:

  • Input Options

    • Input event source: GTK
  • Keystroke Generation

    • Key-stroke generator: uinput

If you previously tried uinput and it did not work,
you must re-test after completing the permission setup.


8. Verification

  1. Open a text-input application (Kate / Firefox / Konsole)
  2. Focus a text field
  3. Click keys on Onboard

Successful behavior:

  • Text appears in the application
  • Keyboard can be shown and hidden repeatedly
  • No need to restart KWin
  • No freezing or dead state
  • Completely avoids Maliit bugs

9. Create a Desktop Launcher (Recommended)

nano ~/.local/share/applications/onboard-x11.desktop

Contents:

[Desktop Entry]
Name=Onboard (Wayland Safe)
Exec=env GDK_BACKEND=x11 onboard
Type=Application
Icon=onboard
Categories=Utility;Accessibility;

You can now:

  • Pin it to the KDE panel
  • Place it on the desktop
  • Use it as a one-click virtual keyboard launcher

10. Limitations and Notes

Known Limitations

  • Does not work on the SDDM login screen
  • Not Wayland-native (runs via XWayland)
  • Elevated input permissions (recommended for personal devices only)

Advantages

  • No swipe-down freeze issue
  • Full Ctrl / Alt / Function key support
  • Works with Fcitx5 (Chewing / Zhuyin)
  • Compatible with Synergy / KVM
  • Stable for long-term use

11. Reverting the Setup (Optional)

sudo rm /etc/udev/rules.d/99-uinput.rules
sudo gpasswd -d hln uinput
sudo reboot

12. Conclusion

On Debian 13 with KDE Plasma Wayland:

Onboard + uinput is currently the only virtual keyboard solution that truly works.

It is not an official or perfect solution,
but KDE is a volunteer-driven community, and expectations should remain realistic.

What matters most is that the problem is solved:
I can now use a keyboard-less tablet to type Taiwanese Mandarin with Zhuyin or English and actually get work done.

Configure WireGuard L3 Routing + NAT on Debian Linux

Preface

The company I work for uses a DrayTek router model that does not support WireGuard. By running WireGuard on a Debian VM instead, we gain:

  • Performance decoupled from router hardware: the VM can scale (CPU/RAM/NIC), giving better crypto throughput.
  • Clean, flexible routing: send only specific subnets (e.g., 192.168.0.0/24, 192.168.10.0/24) through the tunnel—no extra client-side commands.
  • Keep personal internet as-is: regular web traffic does not exit via the company gateway, so external sites don’t see the company IP and your browsing isn’t slowed by the office uplink.
  • Broad client compatibility: easy setup on Windows/macOS/Linux and mobile.

Goal: Let external Windows 11 clients connect via WireGuard and access 192.168.0.0/24 and 192.168.10.0/24 behind a DrayTek router. Approach: Layer-3 routing + NAT (egress interface ens18).


0) Server environment

  • Debian 12
  • KDE Plasma

    installed for GUI convenience. Note: GUI network tools (NetworkManager) affect which commands manage the NIC (e.g., nmcli vs /etc/network/interfaces).


1) Topology & key parameters

  • WG server (Debian VM)

    • Interface: ens18
    • LAN IP: 192.168.0.70/24
    • Gateway: 192.168.0.251 (DrayTek)
    • WG tunnel IP: 10.10.0.1/24
  • Windows 11 client

    • WG tunnel IP: 10.10.0.2/32
  • DrayTek

    • Must port-forward UDP 51820 → 192.168.0.70:51820
    • VLANs 192.168.0.0/24 and 192.168.10.0/24 are inter-routable

1.5) Router / Port Forwarding (DrayTek or your own router)

Your router must forward WireGuard UDP traffic from the internet to your Debian WG server.

DrayTek example (replace with your actual router if different):

  • Type: Port Forward / NAT
  • Protocol: UDP
  • External port: 51820
  • Internal host (server): 192.168.0.70
  • Internal port: 51820
  • Comment: WireGuard
  • Ensure any WAN firewall rule also permits UDP/51820.

Using a different brand/model?
Do the equivalent:

  1. Create a UDP port-forward from the WAN to your WG server’s LAN IP (192.168.0.70) on port 51820.
  2. If your router has a separate firewall, add an allow rule for UDP/51820 inbound.
  3. If you’re behind double NAT (ISP modem + your router), set the forward on both devices or place your router in bridge/DMZ mode on the upstream.
  4. If your ISP uses CGNAT (Common in Japan), inbound port forwarding may not be possible—use a public IP, a VPS relay, or WG peer that can accept inbound connections.

Optional (pure routing instead of NAT): if you remove NAT on the Debian server, add a static route on the router:
Destination: 10.10.0.0/24Next hop: 192.168.0.70.


2) Configure ens18 with NetworkManager (one shot)

nmcli connection add type ethernet ifname ens18 con-name ens18 \
  ipv4.addresses 192.168.0.70/24 ipv4.gateway 192.168.0.251 \
  ipv4.dns "1.1.1.1 8.8.8.8" ipv4.method manual \
  ipv6.method ignore autoconnect yes

nmcli connection up ens18
# sanity checks
ip addr show ens18
ip route get 192.168.0.251   # expect: dev ens18 src 192.168.0.70

We removed/avoid br0; no bridge is required for L3+NAT.


3) Install WireGuard & keys

apt update && apt install -y wireguard iptables tcpdump
( umask 077; wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub )
( umask 077; wg genpsk > /etc/wireguard/psk )   # optional but recommended
chmod 600 /etc/wireguard/server.key /etc/wireguard/psk

4) /etc/wireguard/wg0.conf (NAT egress = ens18)

[Interface]
Address    = 10.10.0.1/24
ListenPort = 51820
PrivateKey = <server.key>

# MASQUERADE traffic from 10.10.0.0/24 out via ens18
PostUp   = iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o ens18 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.10.0.0/24 -o ens18 -j MASQUERADE

# First client
[Peer]
PublicKey    = <client1.pub>
PresharedKey = <psk>            # remove if unused
AllowedIPs   = 10.10.0.2/32

5) Enable IP forwarding (and relax rp_filter to allow forwarding)

cat >/etc/sysctl.d/99-wg.conf <<'EOF'
net.ipv4.ip_forward=1
# Avoid reverse-path drops for wg0→ens18 forwarding
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.wg0.rp_filter=0
net.ipv4.conf.ens18.rp_filter=0
EOF
sysctl --system

6) Firewall (if applicable)

iptables -A INPUT  -p udp --dport 51820 -j ACCEPT
iptables -A FORWARD -i wg0  -o ens18 -j ACCEPT
iptables -A FORWARD -i ens18 -o wg0  -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

7) Bring up & auto-start

wg-quick up wg0
systemctl enable wg-quick@wg0
wg show

8) Windows 11 client client.conf

[Interface]
PrivateKey = <client.key>
Address    = 10.10.0.2/32
DNS        = 192.168.0.251

[Peer]
PublicKey    = <server.pub>
PresharedKey = <psk>                     # remove if unused
Endpoint     = <your_public_IP_or_DDNS>:51820
# Send company subnets through the tunnel
AllowedIPs   = 10.10.0.1/32, 192.168.0.0/24, 192.168.10.0/24
PersistentKeepalive = 25

If the client’s local LAN is also 192.168.0.0/24, prefer precise host routes (/32) to avoid conflicts, or temporarily use 0.0.0.0/0 to verify.


9) Validation

Windows (after connecting):

ping 10.10.0.1
ping 192.168.0.70
ping 192.168.0.251
ping 192.168.0.203
ping 192.168.10.20

Server (while pinging):

iptables -t nat -v -n -L POSTROUTING   # expect MASQUERADE on -o ens18 with growing counters
wg show                                # peer rx/tx increasing
tcpdump -ni wg0 icmp
tcpdump -ni ens18 host 192.168.0.203 and icmp
ip route get 192.168.0.203             # expect dev ens18 src 192.168.0.70

10) Quick troubleshooting

  • Only pinging 10.10.0.1 / 192.168.0.70 works
    Check ip_forward=1, rp_filter=0, and this NAT rule exists:
    -A POSTROUTING -s 10.10.0.0/24 -o ens18 -j MASQUERADE.

  • No 192.168.0.0/24 route on Windows
    Add it in AllowedIPs, or use /32 per-host when the local LAN conflicts.

  • Need VLAN10 access
    Just add 192.168.10.0/24 to the client’s AllowedIPs. DrayTek already routes between VLANs.

  • Prefer pure routing (no NAT)
    Add a static route on DrayTek: dest 10.10.0.0/24 → next-hop 192.168.0.70, then remove the NAT lines from wg0.conf.