SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-29

How to Run Remote Desktop on Linux VPS with xrdp

Run XFCE on your Linux VPS with xrdp, tunnel RDP through SSH instead of exposing port 3389, and see why RustDesk relay no be VPS desktop.

Wetín remote desktop for Linux VPS really mean

Two different products dey answer the search “remote desktop on a Linux VPS”, and if you pick the wrong one, e fit waste your whole afternoon. The first one na remote-access broker. RustDesk self-hosted server na common example. E dey relay session between two machines wey you already own, like your laptop and the PC for your house. The rented server no dey draw any desktop. E only introduce the two ends to each other and forward packets when dem no fit reach each other directly. The second one na real graphical desktop wey dey run for the rented server. For this case, the pixels dey render for the data centre and stream come meet you. Examples na xrdp, VNC (virtual network computing), or a container workspace.

One question dey separate the two. After you make am work, where the mouse pointer dey? If e dey on machine wey you already own, you need broker. If e dey on the VPS itself, you need desktop for the VPS. The second case get most of the space below, because na the case wey plenty guides dey skip.

Which option fit your job

  • RustDesk with your own relay. E dey protect the session make e no pass through public rendezvous server wey strangers dey run, because na you hold the key pair. E no dey protect the machine wey person dey control. Na still the PC wey you install the client on, with whatever password wey that PC get.
  • xrdp over an SSH tunnel or a VPN. E dey protect you from the constant internet-wide scanning of TCP 3389 and password guessing against the RDP login box, because that port no face internet at all. E no dey protect weak account password from anybody wey already get the tunnel.
  • VNC over the same tunnel. E give you desktop session wey fit survive disconnection, using protocol wey old and simpler pass RDP. By itself, e no protect anything. Na the tunnel dey do all the security work, so VNC alone for public port na the worst option for here.
  • A container workspace such as Webtop or Kasm. E give you browser or complete desktop inside container wey you fit throw away and rebuild, so e protect your real machine from anything wey that browser touch. E no protect the host. These images dey run with broad privileges and passwordless sudo inside, so you no suppose trust the container as boundary for hostile workload.

Install xrdp and XFCE for Ubuntu 24.04

VPS server image no dey come with graphical desktop. You go install one, then install xrdp, the open-source server wey dey speak RDP (remote desktop protocol), na the same protocol wey Windows client dey speak. Choose light desktop, and XFCE na the usual choice.

sudo apt update
sudo apt install -y xrdp xorgxrdp xfce4 xfce4-goodies dbus-x11
systemctl is-active xrdp

Ubuntu 24.04 get xrdp 0.9.24 and xorgxrdp for the universe component, as of August 2026. Install xorgxrdp by name even though na only recommended package: na the X server backend wey xrdp dey start for new session, and without am the login box go accept your password, then send you straight back to the login box.

Now tell the session which desktop to start. xrdp dey run /etc/xrdp/startwm.sh, wey dey run ~/.xsession when that file dey exist.

echo "xfce4-session" > ~/.xsession
chmod 644 ~/.xsession

Last, xrdp need read the TLS (transport layer security) key wey e dey offer clients. That file get mode 640 and the ssl-cert group own am.

ls -l /etc/ssl/private/ssl-cert-snakeoil.key
id xrdp

The listing show -rw-r----- 1 root ssl-cert. If id xrdp no print ssl-cert among the groups, run sudo adduser xrdp ssl-cert then sudo systemctl restart xrdp. If you skip that step, xrdp no fit open the key, and /var/log/xrdp.log go record the failure with the snakeoil filename for the line.

Why you no suppose open port 3389 to internet

Everything for internet dey scan TCP 3389 continuously, and RDP login box dey answer every password attempt. No open am. Bind xrdp to loopback address instead, then reach am through tunnel wey you already trust.

Edit /etc/xrdp/xrdp.ini and change the listener for [Globals] section.

[Globals]
port=tcp://.:3389

The shipped file explain this syntax for its own comments: tcp://.:3389 mean 127.0.0.1:3389, while tcp://:3389 mean every interface. Restart and confirm, because typo here fit quietly leave the service for all addresses.

sudo systemctl restart xrdp
ss -tlnp | grep 3389

You want 127.0.0.1:3389. If you see 0.0.0.0:3389, xrdp ignore your edit. Most times, the line end up under another section heading further down the file.

Now open the tunnel from your own machine.

ssh -N -L 3389:127.0.0.1:3389 you@vps.example.com

-N mean “open the connection but run no command”, so the session dey carry the port only. Leave that terminal running and point the RDP client to 127.0.0.1:3389. For Linux client, the software na FreeRDP 3, and the binary name for Ubuntu 24.04 na xfreerdp3:

sudo apt install -y freerdp3-x11
xfreerdp3 /v:127.0.0.1:3389 /u:you /dynamic-resolution +clipboard /sound

For Windows, use the built-in mstsc and enter 127.0.0.1 as the computer. FreeRDP go ask you to trust the certificate the first time you connect, and e go print Do you trust the above certificate? (Y/T/N). This one dey expected with the self-signed snakeoil certificate.

If ssh answer bind [127.0.0.1]:3389: Address already in use, something for your own machine already dey use 3389. Move the local end with ssh -N -L 13389:127.0.0.1:3389 you@vps.example.com and connect to 127.0.0.1:13389.

One tunnel for each person fit become tiring. For team, private network dey better. Put the box behind self-hosted WireGuard VPN, give am tunnel address 10.8.0.1, and set port=tcp://10.8.0.1:3389 so xrdp go answer only inside the VPN. Either way, firewall rule for 3389 no suppose dey at all. If you no sure wetin your current rules allow, start from ufw firewall basics for VPS and check before you connect, no be after.

Remote desktop dey use how much RAM for 2 GB VPS

The desktop wey you choose go decide whether 2 GB plan go work well or become problem. The figures below na rounded typical numbers for memory wey dey use just after login for Ubuntu 24.04. Dem come from published comparisons, no be measurement for your own machine. Measure your own with free -m immediately after you connect.

ChartTypical memory in use after login, Ubuntu 24.04 (published figures)
The data behind this chart
[
  {
    "label": "LXQt",
    "idle_ram_mb": 300
  },
  {
    "label": "XFCE",
    "idle_ram_mb": 400
  },
  {
    "label": "MATE",
    "idle_ram_mb": 500
  },
  {
    "label": "KDE Plasma",
    "idle_ram_mb": 800
  },
  {
    "label": "GNOME",
    "idle_ram_mb": "1,200"
  }
]

For those 5 desktops, the difference na the important part. LXQt dey around 300 MB and XFCE dey around 400 MB, so either one go leave space for browser on 2 GB machine. GNOME need roughly 1,200 MB before you open even one window. For 2 GB, browser go then compete with desktop for the remaining memory.

Browser na the real cost, no be desktop shell. Modern browser dey use between 150 and 400 MB for each active tab, so 2 GB VPS wey dey run XFCE fit handle some tabs, then e go start swapping. Add swap so the machine go slow down instead of killing processes: sudo fallocate -l 2G /swapfile, then sudo chmod 600 /swapfile, sudo mkswap /swapfile, sudo swapon /swapfile, and add matching line for /etc/fstab so e fit survive reboot. When something disappear without warning, run dmesg | grep -i "killed process". That line mean say kernel out-of-memory killer don end the process. Browser na the usual victim.

CPU na the other limit, and e easy to underestimate am. VPS no get GPU, so X go fall back to software rendering through llvmpipe. This mean say CPU go draw every pixel. Scrolling heavy page and playing video go both show as normal CPU load, and frame rate go drop instead of machine freezing. Na the same limit you go meet if you dey wonder whether you fit game for VPS: for anything 3D, the answer na no, and na exactly because of this.

Sound and clipboard for an xrdp session

Ubuntu 24.04 dey use PipeWire for audio, but xrdp sound redirection na PulseAudio-based. So fresh install go give you working video but no sound. Ubuntu package come with the bridge.

sudo apt install -y pipewire-module-xrdp pulseaudio-utils alsa-utils

Log out from the RDP session completely, then log in again, because the module dey load when the session start. Reconnect alone no dey enough. Then check from inside the session:

pactl list short sinks
speaker-test -c 2 -t wav -l 1

You suppose see one sink wey name mention xrdp, and hear the test tone through your client. If no xrdp sink dey, the module no load into this session. Your client also need request audio: na the /sound flag for xfreerdp3, or the "Remote audio" setting under Local Resources for the Windows client.

Text clipboard dey work for both directions once xrdp-chansrv dey run for your session. xrdp dey start am for you. Confirm am with pgrep -a xrdp-chansrv. If copy and paste stop working halfway through a session, that process don die, and reconnecting go start am again. Copying files instead of text na separate channel wey dem call drive redirection: /drive:home,/home/you for xfreerdp3 mounts one local folder inside the remote session.

The polkit popup, and other first-login failures

The most common surprise for first login na dialog wey dey show Authentication is required to create a color managed device. The cause clear. colord service dey ask polkit for permission. polkit dey grant that action silently only to session wey e consider say na local session. RDP session no be local session, so polkit go ask you for password. Ubuntu 24.04 dey ship with polkit 124. E remove the old local authority .pkla files. So any guide wey tell you make you write /etc/polkit-1/localauthority/50-local.d/45-allow-colord.pkla no go work at all for 24.04. Write JavaScript rule instead.

/* /etc/polkit-1/rules.d/45-allow-colord.rules */
polkit.addRule(function(action, subject) {
    if (action.id.indexOf("org.freedesktop.color-manager.") === 0 &&
        subject.isInGroup("sudo")) {
        return polkit.Result.YES;
    }
});

Run sudo systemctl restart polkit then connect again. Two other failures dey important based on the symptom.

The login box takes your password and comes straight back. The session start and die immediately. Read /var/log/xrdp-sesman.log first, then read ~/.xsession-errors for your home directory. If xorgxrdp dey missing, if ~/.xsession name desktop wey you never install, if you no fit write to your home directory, or if disk full, na here dem all end.

You connect and see a grey screen with an X cursor. X start, but desktop no start. Na ~/.xsession again: run xfce4-session manually over SSH and read the error wey e print.

Wetin the self-hosted RustDesk server dey do

RustDesk split into two processes. hbbs na the ID and rendezvous server wey clients dey register with, while hbbr na the relay wey dey carry the session when direct peer-to-peer connection fail. None of dem dey run desktop. Both come from one image, and this na the compose file wey the project publish, with the relay address change to your own host name:

services:
  hbbs:
    container_name: hbbs
    image: rustdesk/rustdesk-server:latest
    command: hbbs -r rustdesk.example.com:21117
    ports:
      - 21115:21115
      - 21116:21116
      - 21116:21116/udp
      - 21118:21118
    volumes:
      - ./data:/root
    restart: unless-stopped
  hbbr:
    container_name: hbbr
    image: rustdesk/rustdesk-server:latest
    command: hbbr
    ports:
      - 21117:21117
      - 21119:21119
    volumes:
      - ./data:/root
    restart: unless-stopped

Start am, then read the public key wey the server generate for its first start:

sudo docker compose up -d
sudo cat ./data/id_ed25519.pub

Every client need your host name and that public key. Enter both under Network settings for the RustDesk client. The matching private key dey inside ./data/id_ed25519. If you delete the data directory, the server go generate fresh key pair. Then every client go need reconfigure with the new key. Back up that directory. If your team go use this to reach every machine instead of using am as weekend experiment, a dedicated RustDesk relay build dey worth following for Ed25519 key handling, pinned image tags instead of latest, and the relay bandwidth wey your plan go eventually pay for.

Firewall must allow these ports directly. hbbs dey use TCP 21115, 21116 and 21118, plus UDP 21116. hbbr dey use TCP 21117 and 21119.

sudo ufw allow 21115/tcp
sudo ufw allow 21116/tcp
sudo ufw allow 21116/udp
sudo ufw allow 21117/tcp
sudo ufw allow 21118/tcp
sudo ufw allow 21119/tcp

Why RustDesk no go behind nginx or Traefik

Readers wey already dey terminate TLS for everything for one reverse proxy fit try this and fail. hbbs and hbbr dey speak their own binary protocols over TCP and UDP, no be HTTP. No Host header dey to use route request, and no HTTP request dey to inspect, so nginx server block or Traefik HTTP router no get anything to match. The UDP listener for 21116 no be HTTP thing for any layer.

Two things dey work. nginx fit forward the TCP ports with a stream block. This one na plain layer 4 forwarding, no be reverse proxy for the usual meaning. Ports 21118 and 21119 carry the websockets wey RustDesk web client dey use. This one na ordinary HTTP, so both fit stay behind your proxy. If you do this, add firewall rules make na only the proxy fit reach 21118 and 21119. hbbs trusts the X-Real-IP header for websocket connections to find the real client address.

A container wey browser dey use once

Sometimes na clean browser you need, with IP wey no dey change, and wey no dey your own machine. Container workspace fit do this with much less software installed. LinuxServer Webtop na the lighter option:

services:
  webtop:
    image: lscr.io/linuxserver/webtop:latest
    container_name: webtop
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Etc/UTC
    volumes:
      - /path/to/data:/config
    ports:
      - 127.0.0.1:3000:3000
      - 127.0.0.1:3001:3001
    shm_size: "1gb"
    restart: unless-stopped

Port 3000 dey serve HTTP, while 3001 dey serve HTTPS. You fit open the desktop for browser tab without any RDP client. Image tags cover XFCE, KDE, MATE, and i3 for different base distributions. The project documentation talk the risk clearly: the container get privileged access to the host and e include terminal with passwordless sudo. So, you no suppose expose am to internet without protection. Na why the ports above bind to 127.0.0.1 instead of publishing for all addresses. You fit reach am through the same SSH tunnel or VPN wey you use for xrdp.

Kasm Workspaces na the same idea, but e bigger well well. E get web console, user accounts, and containers for each session wey reset when the session end. E need more machine resources than small VPS get. As of August 2026, the documented minimum na 2 CPU cores, 4 GB memory, and 50 GB SSD. Each user session dey use 2 cores and 2768 MB by default on top of that. 2 GB plan no go run am. The installation na download plus script:

cd /tmp
curl -O https://kasm-static-content.s3.amazonaws.com/kasm_release_1.17.0.7f020d.tar.gz
tar -xf kasm_release_1.17.0.7f020d.tar.gz
sudo bash kasm_release/install.sh

VNC, and where e still fit

VNC dey send framebuffer updates instead of drawing commands, so e dey feel heavier than RDP for slow link and e no carry sound channel. E useful for one situation: you want desktop session wey go continue to run after you disconnect, and you want the same session back when you return. TigerVNC fit do that. vncserver -localhost yes :1 dey bind Xvnc to 127.0.0.1 for TCP 5901, and e dey reject connection from anywhere else, so you tunnel am exactly like xrdp with ssh -N -L 5901:127.0.0.1:5901 you@vps.example.com. Never publish VNC port. Most VNC servers dey protect password during handshake only, and nothing after that. So if port dey public, anybody fit read the session content for the network.

VPS good desktop dey?

As daily-use machine, no, and reasons plenty. GPU no dey, so CPU dey draw everything. Every keystroke dey wait for network round trip, and 40 ms latency wey feel okay for SSH go noticeable inside text editor. Site compress video once, then RDP encoder compress am again. Your files dey for disk wey you no hold, and heavy desktop use dey chop monthly bandwidth allowance wey dem size for web server.

As disposable machine, e good well-well, and na the same properties explain why. IP address stable and belong to data centre, na wetin you need when service must see consistent address. Machine fit rebuild from image within minutes, so if session pick something bad, e no cost you anything. E dey separate from your real hardware, and e continue to run when you close laptop. Hourly billing make throwaway desktop cheap.

If you never sure wetin you want use the box do, this practical list of wetin VPS good for worth reading before you install desktop for am. And if na one Windows application make you want desktop, compare am with the real differences between Linux and Windows Server first, because the licence dey change the cost of the answer.

FAQ

I fit run remote desktop for 2 GB VPS?

Yes, if you use light desktop. XFCE or LXQt dey use roughly 300 to 400 MB after login, so e leave enough memory for browser with small number of tabs. GNOME or KDE Plasma for 2 GB dey leave almost nothing for applications. Add 2 GB swap file so memory pressure go slow the machine instead of killing processes. If something disappear without message, check dmesg | grep -i "killed process" for the kernel out-of-memory killer.

I suppose open port 3389 for my VPS firewall?

No. Dem dey scan TCP 3389 constantly, and exposed RDP login box dey invite password guessing. Set port=tcp://.:3389 inside /etc/xrdp/xrdp.ini so xrdp go listen only on 127.0.0.1. Confirm am with ss -tlnp | grep 3389, then reach am with ssh -N -L 3389:127.0.0.1:3389 you@vps.example.com. If more than one or two people go use am, bind xrdp to WireGuard address instead of loopback.

Why xrdp dey ask "Authentication is required to create a color managed device"?

The colord service dey ask polkit for permission, and polkit dey grant that action silently only to session wey local user dey sit for. RDP session no be seated session, so e go show password prompt for every login. For Ubuntu 24.04, the old .pkla fix no dey work because polkit 124 drop the local authority files. Create /etc/polkit-1/rules.d/45-allow-colord.rules with JavaScript rule wey return polkit.Result.YES for action ids wey start with org.freedesktop.color-manager., then run sudo systemctl restart polkit.

I fit put self-hosted RustDesk server behind nginx or Traefik?

No be the main service. hbbs and hbbr dey use their own binary protocols instead of HTTP, so no Host header dey available for routing. UDP 21116 no fit pass through HTTP proxy at all. Open TCP 21115 through 21119 and UDP 21116 for firewall, then make clients connect directly. The websocket ports 21118 and 21119 wey web client dey use na HTTP, so dem fit sit behind proxy. If dem dey behind proxy, configure firewall so na only the proxy fit reach dem, because hbbs trusts X-Real-IP for those connections.

Why sound no dey for my xrdp session?

Ubuntu 24.04 dey use PipeWire, but xrdp sound redirection na for PulseAudio, so audio no go dey until you install the bridge. Run sudo apt install -y pipewire-module-xrdp, then log out from the session completely and log in again. The module dey load when session start, and reconnect no go load am. Check with pactl list short sinks for sink wey name xrdp, and make sure client request audio. For xfreerdp3, na /sound flag; for Windows client, select "Remote audio".