While this guide applies to Linux distributions in general, the commands and package names shown here are specifically for Debian/Ubuntu servers and desktops. In most cases, you’ll just have to adapt the apt command and package name for your distribution if you’re not on a Debian based system.
Many guides do two things wrong:
- They install a full-blown desktop on your server, which is overkill.
- They install a VNC server and use it as RDP backend, which is stupid.
Why the things other guides do are bad:
- Installing a full-blown desktop environment on your server is too much, as it will effectively turn your server into a desktop, with login greeter, pulseaudio, bloatware, and possibly freakin’ solitaire! Do you want this for your server? Even if you use tasksel and choose a “mininmal” desktop, it’s still too much.
- Installing VNC as backend defeats the whole point of RDP. You see, RDP and VNC work completely differently: RDP is meant for real remote desktop from client to server, where the client chooses the screen resolution and the server transmits draw commands. The screen is rendered on the client, so to speak. VNC on the other hand streams images of your current session, which is what VNC will connect to and what will determine the screen resolution. But since we’re not going to turn a server into a desktop, VNC wouldn’t even work.
- Note that there’s one use case where a VNC backend makes sense: If it’s an actual desktop computer that already uses a VNC server and you just want to add RDP on top so that Windows users can connect to it without having to install an additional VNC client and instead use the built-in RDP client.
What we’ll be doing instead:
We’ll be installing a virtual display, keyboard and mouse driver which will be controlled by RDP. This way, our client will determine screen resolution and the server will be sending draw commands, the way RDP is meant to be. Each time we connect to RDP a new X session will be started according to the client’s parameters. Furthermore, if your internet connection allows it, it will be so fast you will be able to watch videos through RDP!
We’ll also be putting together our own mini-desktop which will give us just what we need on a server.
Getting started:
We need to install two packages, the virtual display/keyboard/mouse drivers and the RDP server itself.
But first, what driver we’ll be installing depends on your Ubuntu Server version and whether it’s using the HWE kernel or not. If you’re not on HWE you’ll simply install:
sudo apt install xorgxrdp xrdp
If you’re on Ubuntu 18.04 with HWE kernel you’ll instead install:
sudo apt install xorgxrdp-hwe-18.04 xrdp
xorgxrdp is the driver package for the virtual screen, keyboard and mouse whereas xrdp is the actual RDP server.
Installing things for the mini desktop
For our mini desktop we’ll be using Openbox as the window manager, one that’s for example used by the LXDE desktop. However, we won’t install a desktop on top of it. Instead, we’ll be putting it together ourselves.
sudo apt install openbox
If we were to restart xrdp now and connect, we’d be getting a boring black screen. But we want a task bar and perhaps a desktop wallpaper to show off what we can do, and to not get too depressed by a black screen.
For our taskbar we’ll be using tint2 and we’ll be using hsetroot to change the desktop background:
sudo apt install tint2 hsetroot
And for wallpapers, assuming we’re on Ubuntu 18.04 Bionic:
sudo apt install ubuntu-wallpapers-bionic
Now we need to put things together to make our desktop!
Putting the mini desktop together
Now we’ll be editing the Openbox autostart file to start the taskbar and set the desktop background. Use your favorite editor like nano:
sudo nano /etc/xdg/openbox/autostart
And insert the following:
hsetroot -cover /usr/share/backgrounds/Beaver_Wallpaper_Grey_4096x2304.png &
tint2 &
Hit CTRL + S to save and CTRL + X to exit nano.
We can now restart xrdp so it will be aware of openbox and the xinitrc:
sudo systemctl restart xrdp
Installing basic applications
While in theory we’re done here, we still want some basic applications like a terminal emulator and a file browser and a web browser. But we want the minimal possible!
For our terminal needs we’ll be using xterm, and for the file browser we’ll be using xfe which is minimal but enough and is reminiscent of the Windows Explorer. Xfe also includes a minimal text editor and image viewer. For the browser we could use something minimal like midori but I’ll go for Firefox this time:
sudo apt install xterm xfe firefox
Installing the RDP client
On the client side, we’ll be installing Remmina with the RDP plugin like so:
sudo apt install remmina-plugin-rdp
SSH Tunnel
Now, if you’ll just be using this on your trusted intranet, all is done and ready but since TLS support isn’t that great yet on the FOSS RDP side, if you want to connect through the internet you should be using an SSH tunnel. Remmina can do this for you, but in my case I use a hardware security key which doesn’t work with Remmina, so I’ll be showing you how to manually set up the SSH tunnel:
ssh -L 3389:127.0.0.1:3389 username@server
This will connect to your server through SSH, and create a local listener at port 3389 (the default RDP port) and forward all incoming connections through SSH to (from the server perspective) to localhost on port 3389.
Connecting with Remmina
Start Remmina, click on the plus and set the name to something like “Localhost SSH” choose RDP as protocol and set the server to localhost and then RemoteFX as color depth. For resolution, assuming you want full screen, click on the three dots right to the selector and add your desktop resolution and choose that. Now click save.
Note: If your SSH server is using password or keyfile authentication you can instead configure the SSH tunnel in the SSH tunnel tab. However, password authentication is discouraged.
Double-click on the saved connection to connect. If you can’t see it, sometimes Remmina bugs. Close and re-open Remmina. After connecting you will see something like a login window. For session choose Xorg and just enter the username and password of the server’s user you want to connect as (the same one we set up Openbox for).
And now you should be logged in to your shiny new, minimal desktop! To start applications, right click on the desktop to bring up the Openbox menu.
Conclusion
And this is how you do it the right way, without bloat (except for the wallpaper).
Of course, you can now configure xrdp to require network level authentication and tweak things to your liking but I just wanted to give you the basics on how to properly set up an RDP server on Ubuntu without a full desktop or other bloat, or nonsense VNC backend.
And that’s all there is to it!
Notes
I noticed on Ubuntu Server 20.04 the package xorgxrdp seems to install a full-blown desktop which is madness and should probably be reported as a bug… On Ubuntu Server 18.04 it does not, however.
hi, so I installed OpenBox first (~80mb), then I try to install xrdp, but it appears to want to install a ton of packages (880mb disk space). I have a very limited use case (launch a single java app – no desktop – this works fine), is there a way to only install openbox without all the bloat that xrdp believes its dependant on? for instance I don’t need the ubuntu wallpapers, control centre etc. so why is this a ‘dependency’. Any ideas?
Full list of dependencies: (Ubuntu 20.04 LTS)
acl apg aptdaemon aptdaemon-data aspell aspell-en avahi-daemon
avahi-utils bluez bubblewrap cheese-common colord colord-data cpp
cpp-9 cracklib-runtime crda cups-pk-helper dbus-x11 dconf-cli
desktop-file-utils dictionaries-common dns-root-data dnsmasq-base
docbook-xml emacsen-common enchant-2 evolution-data-server
evolution-data-server-common fprintd gcc-9-base gcr gdm3 geoclue-2.0
gir1.2-accountsservice-1.0 gir1.2-atk-1.0 gir1.2-atspi-2.0
gir1.2-freedesktop gir1.2-gck-1 gir1.2-gcr-3
gir1.2-gdesktopenums-3.0 gir1.2-gdkpixbuf-2.0 gir1.2-gdm-1.0
gir1.2-geoclue-2.0 gir1.2-gnomebluetooth-1.0 gir1.2-gnomedesktop-3.0
gir1.2-graphene-1.0 gir1.2-gtk-3.0 gir1.2-gweather-3.0
gir1.2-ibus-1.0 gir1.2-json-1.0 gir1.2-mutter-6 gir1.2-nm-1.0
gir1.2-nma-1.0 gir1.2-notify-0.7 gir1.2-pango-1.0 gir1.2-polkit-1.0
gir1.2-rsvg-2.0 gir1.2-secret-1 gir1.2-soup-2.4
gir1.2-upowerglib-1.0 gir1.2-vte-2.91 gjs gkbd-capplet
gnome-control-center gnome-control-center-data
gnome-control-center-faces gnome-desktop3-data gnome-keyring
gnome-keyring-pkcs11 gnome-menus gnome-online-accounts
gnome-session-bin gnome-session-common gnome-settings-daemon
gnome-settings-daemon-common gnome-shell gnome-shell-common
gnome-startup-applications gnome-terminal gnome-terminal-data
gnome-user-docs gstreamer1.0-clutter-3.0 gstreamer1.0-gl
gstreamer1.0-plugins-base gstreamer1.0-plugins-good
gstreamer1.0-pulseaudio gstreamer1.0-x gvfs gvfs-common gvfs-daemons
gvfs-libs hunspell-en-us ibus ibus-data ibus-gtk ibus-gtk3
iio-sensor-proxy im-config ippusbxd iw language-selector-gnome
libaa1 libappindicator3-1 libasound2-plugins libaspell15 libasyncns0
libatasmart4 libavahi-core7 libavahi-glib1 libavc1394-0
libblockdev-crypto2 libblockdev-fs2 libblockdev-loop2
libblockdev-part-err2 libblockdev-part2 libblockdev-swap2
libblockdev-utils2 libblockdev2 libbluetooth3 libboost-thread1.71.0
libcaca0 libcamel-1.2-62 libcanberra-gtk3-0 libcanberra-gtk3-module
libcanberra-pulse libcdparanoia0 libcheese-gtk25 libcheese8
libclutter-1.0-0 libclutter-1.0-common libclutter-gst-3.0-0
libclutter-gtk-1.0-0 libcogl-common libcogl-pango20 libcogl-path20
libcogl20 libcolord-gtk1 libcolorhug2 libcrack2 libdaemon0
libdbus-glib-1-2 libdbusmenu-glib4 libdbusmenu-gtk3-4 libdrm-amdgpu1
libdrm-intel1 libdrm-nouveau2 libdrm-radeon1 libdv4
libebackend-1.2-10 libebook-1.2-20 libebook-contacts-1.2-3
libecal-2.0-1 libedata-book-1.2-26 libedata-cal-2.0-1
libedataserver-1.2-24 libedataserverui-1.2-2 libegl-mesa0 libegl1
libenchant-2-2 libevdev2 libexif12 libflac8 libfontenc1
libfprint-2-2 libfprint-2-tod1 libgbm1 libgck-1-0 libgcr-base-3-1
libgcr-ui-3-1 libgd3 libgdata-common libgdata22 libgdm1 libgee-0.8-2
libgeoclue-2-0 libgeocode-glib0 libgjs0g libgl1 libgl1-mesa-dri
libglapi-mesa libgles2 libglu1-mesa libglvnd0 libglx-mesa0 libglx0
libgnome-autoar-0-0 libgnome-bluetooth13 libgnome-desktop-3-19
libgnomekbd-common libgnomekbd8 libgoa-1.0-0b libgoa-1.0-common
libgoa-backend-1.0-1 libgomp1 libgphoto2-6 libgphoto2-l10n
libgphoto2-port12 libgraphene-1.0-0 libgsound0 libgssdp-1.2-0
libgstreamer-gl1.0-0 libgstreamer-plugins-base1.0-0
libgstreamer-plugins-good1.0-0 libgtop-2.0-11 libgtop2-common
libgupnp-1.2-0 libgupnp-av-1.0-2 libgupnp-dlna-2.0-3
libgweather-3-16 libgweather-common libharfbuzz-icu0
libhunspell-1.7-0 libhyphen0 libibus-1.0-5 libical3 libidn11
libiec61883-0 libieee1284-3 libimobiledevice6 libinput-bin
libinput10 libisl22 libjack-jackd2-0 libjansson4
libjavascriptcoregtk-4.0-18 libldb2 libllvm9 libmbim-glib4
libmbim-proxy libmediaart-2.0-0 libmm-glib0 libmozjs-68-0
libmp3lame0 libmpc3 libmpg123-0 libmtdev1 libmutter-6-0
libmysqlclient21 libnautilus-extension1a libndp0 libnl-3-200
libnl-genl-3-200 libnl-route-3-200 libnm0 libnma0 libnotify4
libnss-mdns libopus0 liborc-0.4-0 libpam-fprintd
libpam-gnome-keyring libparted-fs-resize0 libpciaccess0
libphonenumber7 libplist3 libprotobuf17 libpulse-mainloop-glib0
libpulse0 libpulsedsp libpwquality-common libpwquality1 libqmi-glib5
libqmi-proxy libraw1394-11 librygel-core-2.6-2 librygel-db-2.6-2
librygel-renderer-2.6-2 librygel-server-2.6-2 libsamplerate0 libsane
libsane-common libsbc1 libsecret-1-0 libsecret-common
libsensors-config libsensors5 libshout3 libsmbclient libsnapd-glib1
libsndfile1 libsnmp-base libsnmp35 libsoxr0 libspeex1 libspeexdsp1
libtag1v5 libtag1v5-vanilla libteamdctl0 libtheora0 libtwolame0
libudisks2-0 libupower-glib3 libusbmuxd6 libv4l-0 libv4lconvert0
libvisual-0.4-0 libvolume-key1 libvorbisenc2 libvpx6 libvte-2.91-0
libvte-2.91-common libvulkan1 libwacom-bin libwacom-common libwacom2
libwavpack1 libwayland-server0 libwebkit2gtk-4.0-37 libwebpdemux2
libwebrtc-audio-processing1 libwhoopsie-preferences0 libwhoopsie0
libwoff1 libxatracker2 libxaw7 libxcb-dri2-0 libxcb-dri3-0
libxcb-glx0 libxcb-icccm4 libxcb-image0 libxcb-keysyms1
libxcb-present0 libxcb-randr0 libxcb-render-util0 libxcb-res0
libxcb-shape0 libxcb-sync1 libxcb-xfixes0 libxcb-xkb1 libxcb-xv0
libxfont2 libxkbcommon-x11-0 libxkbfile1 libxklavier16 libxmu6
libxpm4 libxshmfence1 libxss1 libxt6 libxv1 libxvmc1 libxxf86dga1
libxxf86vm1 libyelp0 mesa-vulkan-drivers
mobile-broadband-provider-info modemmanager mousetweaks mutter
mutter-common nautilus-extension-gnome-terminal network-manager
network-manager-gnome network-manager-pptp p11-kit p11-kit-modules
pinentry-gnome3 ppp pptp-linux pulseaudio
pulseaudio-module-bluetooth pulseaudio-utils python3-aptdaemon
python3-aptdaemon.gtk3widgets python3-cairo python3-cups
python3-cupshelpers python3-defer python3-ibus-1.0
python3-macaroonbakery python3-protobuf python3-rfc3339
python3-talloc python3-tz rtkit rygel samba-libs sane-utils
session-migration sgml-base sgml-data switcheroo-control
system-config-printer system-config-printer-common
system-config-printer-udev ubuntu-docs ubuntu-session
ubuntu-wallpapers ubuntu-wallpapers-focal udisks2 update-inetd
upower usb-modeswitch usb-modeswitch-data usbmuxd wamerican
whoopsie-preferences wireless-regdb wpasupplicant x11-apps
x11-session-utils x11-utils x11-xkb-utils x11-xserver-utils xbitmaps
xdg-dbus-proxy xfonts-base xfonts-encodings xfonts-scalable
xfonts-utils xinit xinput xml-core xorg xorg-docs-core xorgxrdp
xserver-common xserver-xephyr xserver-xorg xserver-xorg-core
xserver-xorg-legacy xserver-xorg-video-all xserver-xorg-video-amdgpu
xserver-xorg-video-ati xserver-xorg-video-fbdev
xserver-xorg-video-intel xserver-xorg-video-nouveau
xserver-xorg-video-qxl xserver-xorg-video-radeon
xserver-xorg-video-vesa xserver-xorg-video-vmware xwayland
yaru-theme-gnome-shell yelp yelp-xsl zenity zenity-common
sorry just seen your very last comment in the blog!!! xrdp does the same. maybe I’ll downgrade to Ubunut 18!
I suggest trying it out in a VM first to be safe. But I agree that xrdp is having a lot of bloat as dependencies in 20.04 but IIRC that was not the case in 18.04 but I cannot guarantee that. An alternative suggestion: Compiling xrdp from source perhaps? That way you could bypass the dependency madness.