A headless Linux VPS is great for servers, but sometimes you just want a real desktop. A browser to test something, a file manager to drag files around, GIMP for a quick image edit, or a stable scraping/automation session running 24/7. RDP is the cheapest way to get there - the Microsoft Remote Desktop client ships with every Windows install and has free, polished apps on Mac, iOS, and Android.
xRDP is the open-source Linux implementation of the RDP protocol. It bridges any X11 desktop environment (XFCE, MATE, GNOME, KDE) to an RDP session, so you can log in from any RDP client and get a real graphical session on your Ubuntu box.
This guide walks through a production-ready setup on a single VPS: a lightweight desktop, xRDP, a remote-only user, SSH tunneling instead of an open 3389, fail2ban for brute-force protection, and performance tuning that actually moves the needle.
xrdp, then tell it to launch XFCE for the sessionrdp user and put it in the ssl-cert group3389 to the internet - tunnel it over SSH or expose it only on a Tailscale or WireGuard interfaceTotal time: about 20 minutes.
mstsc.exe on Windows, Microsoft Remote Desktop on Mac/iOS, Remmina on Linux, or the Microsoft RD app on AndroidxRDP itself is light, but the desktop environment matters. XFCE idles around 250 MB and stays snappy on a single vCPU. GNOME is closer to 1.5 GB and will feel sluggish on a small VPS. This guide uses XFCE.
Always start from a clean baseline.
sudo apt update
sudo apt upgrade -y
sudo apt install -y curl ufw
If this is a brand new VPS, also walk through SSH hardening with fail2ban before you go any further. Everything below assumes the SSH side is already locked down.
XFCE is the sweet spot for remote desktops - small enough to run on a 1 GB box, complete enough to feel like a real Linux desktop.
sudo apt install -y xfce4 xfce4-goodies dbus-x11
This pulls in about 700 MB. While it installs, Ubuntu may ask which display manager to use - pick lightdm if prompted, but you won't actually be logging in at the VPS console.
sudo apt install -y xrdp
The installer creates a system user xrdp and starts the service on TCP 3389. Confirm it's running:
systemctl status xrdp
You should see active (running). If it's not, start it:
sudo systemctl enable --now xrdp
By default xRDP launches whatever your shell's ~/.xsession says, which is usually nothing. Tell it explicitly to start XFCE for every session.
Create or replace /etc/xrdp/startwm.sh with:
sudo tee /etc/xrdp/startwm.sh > /dev/null <<'EOF'
#!/bin/sh
if [ -r /etc/default/locale ]; then
. /etc/default/locale
export LANG LANGUAGE
fi
startxfce4
EOF
sudo chmod +x /etc/xrdp/startwm.sh
The original Debian script has logic that tries to launch GNOME and falls back through a chain of desktops. Replacing it with startxfce4 removes that guesswork and makes session startup reliable.
Restart xRDP so it picks up the change:
sudo systemctl restart xrdp
Logging in over RDP as your sudo-enabled admin user is sloppy. Create a separate account that's allowed to RDP but not to escalate to root.
sudo adduser rdp
sudo adduser rdp ssl-cert
The ssl-cert group lets xRDP read its TLS certificate when the user opens a session - otherwise you get a black screen and the logs complain about /etc/ssl/private/ssl-cert-snakeoil.key permissions.
Set a strong password during adduser. Don't reuse your SSH password. RDP exposes the password to brute force, so use something long and random and store it in a password manager.
If you want this user to also have a sudoers entry for one specific task (say, mounting a shared folder), add a single line via visudo rather than handing them full sudo.
This is the single most important step in the guide. The default xRDP install listens on 0.0.0.0:3389. If you open that in your firewall, the entire internet starts brute-forcing your VPS within hours. Every common username and password combination gets tried, all day, every day.
You have three reasonable options. Pick one - never expose 3389 to the internet without one of these.
Tunnel RDP over your already-hardened SSH connection. xRDP listens only on 127.0.0.1, and your client connects to localhost:3389 after opening the tunnel.
Bind xRDP to localhost only. Edit /etc/xrdp/xrdp.ini and change the port line:
[Globals]
port=tcp://127.0.0.1:3389
Restart xRDP:
sudo systemctl restart xrdp
On your local machine, open the tunnel:
ssh -L 3389:127.0.0.1:3389 [email protected]
Now point your RDP client at localhost:3389. Traffic flows over the encrypted SSH tunnel; xRDP has zero exposure on the public interface. Make sure your UFW rules allow SSH but not 3389:
sudo ufw allow 22/tcp
sudo ufw enable
Put the VPS on a private overlay network and let xRDP listen on that interface only. See our guides on Tailscale and WireGuard for the network side.
Once Tailscale is up, find the tailnet IP of the VPS (e.g. 100.x.y.z) and bind xRDP to it:
[Globals]
port=tcp://100.x.y.z:3389
UFW stays closed on 3389 for the public interface. Only devices on the tailnet can reach the port.
If you have a single static office IP and don't want to fiddle with tunnels, allowlist just that one address:
sudo ufw allow from 203.0.113.10 to any port 3389 proto tcp
Be honest with yourself about how static that IP really is. Most home connections rotate addresses periodically, and the day yours flips you'll be locked out.
Even with the public port closed, defense in depth is cheap. If you ever flip to Option B or C, fail2ban catches anything that slips through.
Install fail2ban if you haven't already (see our SSH hardening guide for the full setup):
sudo apt install -y fail2ban
xRDP logs to /var/log/xrdp.log. Create /etc/fail2ban/filter.d/xrdp.conf:
[Definition]
failregex = ^.*\[ERROR\] authentication failed.*from <ADDR>.*$
^.*connection refused from <ADDR>.*$
ignoreregex =
Then create /etc/fail2ban/jail.d/xrdp.conf:
[xrdp]
enabled = true
port = 3389
filter = xrdp
logpath = /var/log/xrdp.log
maxretry = 4
findtime = 10m
bantime = 1h
Reload fail2ban:
sudo systemctl restart fail2ban
sudo fail2ban-client status xrdp
Four failed logins in 10 minutes earns an IP a one-hour ban. Adjust to taste.
xRDP's defaults are conservative. On a small VPS over a slow link, a few tweaks make the difference between "feels like a desktop" and "feels like a slideshow."
Edit /etc/xrdp/xrdp.ini and look at the [Globals] section:
[Globals]
port=tcp://127.0.0.1:3389
crypt_level=high
allow_channels=true
max_bpp=24
new_cursors=true
use_compression=yes
A few notes on what these do:
max_bpp=24 caps color depth at 24 bits. Going to 32 doesn't visibly help anything and burns bandwidth. Drop to 16 for laggy mobile connections.use_compression=yes enables bitmap compression. Big win over residential uplinks.crypt_level=high keeps the RDP connection encrypted with TLS even when you tunnel over SSH. Belt and suspenders.You can also set per-session defaults in the same file under [Xorg] to lower resolution on connect.
On the client side, settings matter just as much. In mstsc.exe:
LAN (10 Mbps or higher) only if you actually have it; otherwise pick Broadband High (2 Mbps - 10 Mbps).1600x900 is the sweet spot - smaller than your local display, still usable.Apply, restart xRDP, reconnect. The session should feel noticeably more responsive.
Out of the box, clipboard sync between your client and the xRDP session is fragile. A small package fixes it for XFCE:
sudo apt install -y xclip
Then enable the clipboard channel in xrdp.ini (it's on by default in modern builds, but worth confirming):
allow_channels=true
For file transfer, the cleanest path on Windows is mstsc's built-in drive redirection. In the RDP client open Show Options, then Local Resources, then More, and check Drives. The selected drives mount inside the xRDP session under ~/thinclient_drives/. Drag and drop works once you navigate there in Thunar.
On Mac/iOS, the official Microsoft Remote Desktop client supports folder redirection under the connection's Folders tab. Same idea: select a local folder, it appears inside the session.
Windows - press Win+R, type mstsc, hit enter. Server: localhost:3389 if you tunneled over SSH, otherwise the VPS hostname. Username: rdp. Save the connection as a .rdp file once it works.
Mac - install the Microsoft Remote Desktop app from the Mac App Store, click the + button, fill in the PC name, save, double-click to connect.
iOS / iPadOS - same Microsoft Remote Desktop app from the App Store. iPads with a Magic Keyboard or Bluetooth mouse give you a real desktop experience that's genuinely useful for sysadmin work.
Android - the Microsoft RD app on the Play Store works well, especially in landscape on a tablet.
Linux - Remmina is the most polished client. It supports SSH tunneling natively, so you don't have to keep a separate ssh -L running. Set up the connection once and tick the SSH tunnel box.
For full-screen and resolution tips per client, our Connect to RDP From Any Device post covers the GUI details.
Black screen after login, then a disconnect. Almost always the ssl-cert group permissions. Run sudo adduser rdp ssl-cert and reconnect.
startxfce4: command not found in ~/.xsession-errors. XFCE didn't install cleanly. Run sudo apt install --reinstall xfce4 and confirm which startxfce4 returns a path.
You can log in once, then it hangs on subsequent connections. A stale session is still bound to the user. SSH in and run sudo pkill -KILL -u rdp, then reconnect.
Two-monitor support is missing. xRDP supports multi-monitor only with Xorg as the backend, which is the default in 22.04+. In your RDP client enable Use all monitors for the remote session.
fail2ban isn't catching anything. Tail /var/log/xrdp.log and confirm the failed-auth format matches the failregex. xRDP's log format changes between versions; adjust the regex if needed and sudo fail2ban-client status xrdp to verify.
The session feels usable for ten seconds then slows to a crawl. Almost always your VPS is swapping. Run htop and check the swap line. Either upgrade to more RAM or drop XFCE for LXQt, which idles around 120 MB.
That's the whole stack. Twenty minutes of setup gets you a real Linux desktop reachable from anywhere - encrypted, brute-force protected, and snappy enough to actually work in.
Need a VPS with enough RAM to host a remote desktop comfortably? Our Linux plans include fast NVMe storage, IPv6, and snapshots out of the box. See the options.