A Wayland desktop per account on your server, over RDP.

One apt install on Debian 13 or Ubuntu 26.04 (amd64 or arm64). Every account that logs in over RDP gets its own headless sway session with a shell, kept alive across disconnects, with memory ceilings and a refusal when the machine is full. H.264 over RDP, no GPU needed. Free software, early. Tested with FreeRDP and the Windows App on macOS. Source on GitHub.

$ curl -fsSLO https://waydesk.dev/apt/waydesk-repo.deb
$ sudo apt install ./waydesk-repo.deb
$ sudo apt install waydesk
$ sudo waydesk-setup-admin
$ sudo waydesk-admin status
Memory    7.6 GB in all, 504 MB available
Swap      zram0 7.6 GB (7.5 GB used)
Load      1.01 1.26 1.29 (4 cores)
Sessions  7 running; a new one would be REFUSED: 504 MB available, 1161 MB needed

ACCOUNT   SESSION  MEMORY  SWAP    CPU  LIMIT   PROGRAMS (resident)
u1        running  416 MB  1.0 GB   3%  3.8 GB  code 194 MB, qs 132 MB, chrome 120 MB
u2        running  472 MB  1.0 GB   4%  3.8 GB  code 263 MB, qs 133 MB, chrome 126 MB
u3        running  438 MB  1.1 GB   3%  3.8 GB  code 186 MB, chrome 176 MB, qs 134 MB
u4        running  455 MB  1.1 GB   3%  3.8 GB  chrome 219 MB, code 206 MB, qs 138 MB
u5        running  477 MB  1.0 GB   4%  3.8 GB  chrome 277 MB, code 252 MB, qs 138 MB
u6        running  858 MB  672 MB   7%  3.8 GB  code 529 MB, chrome 437 MB, qs 173 MB
u7        running  1.5 GB   95 MB   4%  3.8 GB  code 862 MB, chrome 793 MB, qs 265 MB

Real output: Ubuntu 26.04, 4 vCPU, 7.6 GB, seven accounts each with Chromium, LibreOffice Calc and VS Code open, 2026-10-24. Accounts that were idle for a while have most of their memory in compressed swap, which is why the eighth one is refused only now.

Install

Run as an administrator of the server. The port listens on 127.0.0.1 only; see reaching it.

1. Add the repository

One small package: the signing key, the apt source for your release (trixie or resolute, chosen from VERSION_CODENAME) and a pin so these packages win over third-party builds of the same names.

$ curl -fsSLO https://waydesk.dev/apt/waydesk-repo.deb
$ sudo apt install ./waydesk-repo.deb

2. Check the key

This first download cannot be verified by the repository itself. Compare the key you got with this fingerprint (ed25519, expires 2029-10-21):

$ gpg --show-keys --with-fingerprint /usr/share/keyrings/waydesk-archive-keyring.gpg
C646 72B2 5ECD 4773 196C  3958 9D8E 8874 E676 17F4

If it differs, stop and tell us.

3. Install

$ sudo apt update
$ sudo apt install waydesk

Installs the RDP daemon, the root helper, waydesk-admin, our sway build, zram swap and the desktop package. waydesk-server alone is enough for a session with a plain terminal (a minimal sway config ships with it).

4. Shell and first administrator

$ sudo waydesk-setup           # wizard: shell, Chromium, LibreOffice, VS Code, ...
$ sudo waydesk-setup-admin     # the account that logs in over RDP and manages the others

There is no default account and no default password. waydesk-setup-admin prints the certificate fingerprint to expect on the first connection.

Support

Distributionamd64arm64Notes
Debian 13 (trixie)testedtestedclean install from the published repository, RDP session with the shell, Chromium, LibreOffice, VS Code
Ubuntu 26.04 LTS (resolute)testedtestedsway, quickshell and pcmanfm-qt are our builds under /usr/lib/waydesk; Chromium and Firefox come from Flathub (no Chromium deb)
Ubuntu 24.04, Fedora, Archnot yetdifferent Qt and wlroots; no rpm yet

"Tested": a clean server, SSH hardened first, then exactly the four steps above, an RDP login from FreeRDP and a look at the session. Ubuntu's own xfreerdp3 (3.32) cannot decode our H.264 stream; Debian 13's client (3.15) can. The Windows App on macOS was also used; Microsoft Remote Desktop and mstsc on Windows were not tested.

Architecture

How a login reaches a session The client connects with TLS and RDP to the waydesk daemon, which has no privileges. The daemon asks the root helper, over a unix socket, to check the password and start a session. The helper starts the session as the account itself. The session sends screen, input, audio and clipboard back to the daemon. client FreeRDP, Windows App TLS, RDP network-facing, no privileges waydesk.service runs as: waydesk TLS, RDP, H.264 framing no PAM, no user files unix socket, peer credentials checked root, no network waydesk-helper.service runs as: root PAM check, memory admission starts the session as that account the account itself own logind session, own cgroup waydesk-session@UID runs as: the account sway (headless) capture, H.264 encode, input audio, clipboard shell, apps screen, input, audio, clipboard
PartRuns asDoesNever does
waydeskwaydeskTLS, RDP protocol, channel framing, rate limits per addresstouch user files, call PAM, keep passwords after the check
waydesk-helperrootchecks the password through PAM, starts and finds sessions, admits or refuses by memoryparse RDP or TLS, listen on the network
waydesk-session@UIDthe accountits sway, capture, encoder, input, audio, clipboard, hooksanything as another user

A session is a systemd unit of the account, so it survives a disconnect, sits in user-UID.slice (limits there apply) and is killed with its control group. A second login from the same account takes the running session over. Design notes: docs/MULTIUSER-DESIGN.md in the repository.

Measurements

All figures are from our own servers (Hetzner cloud, no GPU), with the method next to them. Full tables and the failed attempts are in docs/MEASUREMENTS.md in the repository.

Heavy users on 8 GB, 4 vCPU (x86_64)

Available memory (free's figure) after each user has logged in over RDP and opened Chromium (Wikipedia, BBC News, debian.org), LibreOffice Calc with a 300,000 x 10 CSV and VS Code on a source tree.

usersDebian 13Ubuntu 26.04
15.4 GB5.2 GB
23.9 GB3.4 GB
32.4 GB2.1 GB
41.6 GB2.2 GB
51.7 GB2.0 GB
61.6 GB1.7 GB
71.6 GB810 MB, next refused
81.1 GBrefused
9refused

Method: one account at a time, 70 s after its last application started, applications left idle (an upper bound for idle load, no figure for active work). The server refuses a session when less than about 1.1 GB is available; the refusal reaches the client as ERRINFO_OUT_OF_MEMORY. No out-of-memory kill in any run; memory pressure ("some", 60 s average) 0.3 to 2.4 %. One heavy user costs 1.85 GB (Debian) to 2.0 GB (Ubuntu, Flatpak builds of Chromium and VS Code, a 30 MB tree instead of 25 MB).

Compressed swap

zram with zstd compresses this load about 4.5 to 5 : 1 (Ubuntu: 6.6 GB of data in 1.3 GB of RAM at seven users). The package sizes the device like the RAM (at most 16 GiB). With Debian's default of half the RAM, six heavy users fit instead of eight.

Other figures

WhatResultMethod
idle session with the shell430 to 600 MBcgroup of the account, Debian 13 arm64 and Ubuntu 26.04 x86, nothing else open
browser users on arm64, 3.7 GB4 fit, the 5th refused (754 MB available, 900 MB needed)Chromium, same three pages, one account at a time
moving screens, 2 vCPUtwo busy screens use about 90 % of the CPU, three drop to a choppy frame ratevideo in a window, software H.264 (no hardware encoder)
stalled clientconnection closed 80 s after the freeze (TCP_USER_TIMEOUT 60 s)client frozen but answering zero-window probes, Debian 13 arm64

Not measured: CPU under active work (a build, a recalculation), typing and scrolling latency, frame rate on x86 with moving screens.

Security

Reaching it

Do not put the RDP port on the internet. The daemon binds 127.0.0.1:3389 and the login is the account's own password, checked by PAM, sent inside TLS with a self-signed certificate (PAM cannot do NLA). Anyone who can intercept an unchecked certificate can read passwords, so check the fingerprint on first use.

$ ssh -N -L 13389:127.0.0.1:3389 you@server    # then connect the client to localhost:13389

For several people: WireGuard, a key per device, RDP bound to the VPN address, only UDP 51820 open. docs/WIREGUARD.md has the configuration (installed as /usr/share/doc/waydesk-server/WIREGUARD.md).

Threat model

Open items

Limits

Reference

Packages

waydesk

metapackage: server and desktop

waydesk-server

daemon, root helper, waydesk-admin, waydesk-setup-admin, PAM services, units, zram and sysctl drop-ins, minimal sway config

waydesk-desktop

sway config, setup wizard (waydesk-setup), per-account shell setup, administrator panels

waydesk-shell, waydesk-quickshell

DankMaterialShell 2.0.0 built by us with our patches; quickshell 0.3.0, built per distribution

waydesk-sway

sway 1.11 with wlroots 0.19.3 linked statically and the title-bar button patch, under /usr/lib/waydesk/bin; built per distribution (+deb13, +ubuntu26.04)

pcmanfm-qt

file manager with our patches, built per distribution

waydesk-repo

the key, the apt source template and the pin

Commands: administration

waydesk-admin status [--json]

memory, swap, load, running sessions and what each account uses

waydesk-admin users list [--json]

accounts that may log in

waydesk-admin users add NAME [--admin] [--sudo] [--no-rdp]

create an account; prints a generated password

waydesk-admin users passwd NAME [--stdin]

set a password (read from stdin, never from an argument)

waydesk-admin users remove NAME [--delete-home]

remove an account

waydesk-admin users enable NAME | disable NAME [--keep-session]

allow or stop RDP logins for an account

waydesk-admin users admin NAME on|off

Flatpak installs without a password

waydesk-admin users sudo NAME on|off

membership of the sudo group

waydesk-admin users limit NAME [show | reset | --memory SIZE] [--swap SIZE] [--cpu-weight N] [--tasks N]

per-account ceilings

waydesk-admin sessions list [--json] | end NAME

running sessions; ending one closes that account's desktop

Commands: setup

waydesk-setup [--list | --check]

choose and install shell, browsers, office, developer and creative apps

waydesk-setup-admin [--name NAME [--password-stdin]] [--existing NAME]

the first administrator: RDP access, panels, Flatpak installs without a password

Units and ports

waydesk.service

the daemon: TLS, RDP, framing (user waydesk)

waydesk-helper.service

the root helper: password check, session start, memory admission

waydesk-session@UID.service

one per account: its sway, capture, encoder and shell

127.0.0.1:3389

RDP over TLS; reach it through a tunnel or a VPN

Files

/etc/default/waydesk

daemon and session options

/etc/pam.d/waydesk-server

PAM service for the login check

/etc/pam.d/waydesk-session

PAM service that gives the session a logind session

/etc/xdg/waydesk/sway.conf

the session's sway config (fallback: /usr/share/waydesk/sway-minimal.conf)

/usr/lib/waydesk/bin

private sway, swaymsg and swaybar; first in the session's PATH

/run/waydesk/

helper and session sockets

/etc/apt/sources.list.d/waydesk.sources

the repository, written for your release

/etc/apt/preferences.d/waydesk

the pin: these packages win over third-party builds of the same names

/usr/lib/systemd/zram-generator.conf.d/50-waydesk.conf

zram as large as the RAM, at most 16 GiB

Key

Packages and indexes are signed with one ed25519 key, fingerprint C64672B25ECD4773196C39589D8E8874E67617F4, expires 2029-10-21. The public key is /apt/waydesk-archive-keyring.gpg.

Changelog

2026-10-24
Ubuntu 26.04 checked from the repository; dms.sh no longer adds Debian backports when the Waydesk repository is configured (it broke apt update on Ubuntu); a client that stops reading is dropped after 60 s; waydesk-server alone starts a session with a minimal sway config; the Flathub Chromium opens without its terms window and keyring prompt; seven heavy users fit on 8 GB.
2026-10-23
Two suites, trixie and resolute, from one pool; private sway and quickshell under /usr/lib/waydesk; Flathub fallback for browsers; heavy-user capacity on Debian; fix for a publishing tool that deleted live packages.
2026-10-22
Signed apt repository, waydesk-repo and waydesk-setup-admin; our own build of the shell as a package; refused logins tell the client why.
2026-10-21
Memory admission, account ceilings and zram; patched sway with title-bar buttons; dock; Chromium as the default browser; third internal security pass.
2026-10-20
Renamed to Waydesk. Desktop icons, themed Qt and Flatpak apps, logind session per account.
2026-10-15 to 19
RDP server for sway, then multi-user daemon, helper and sessions; DankMaterialShell on sway.