Converting Ubuntu Desktop to a Headless Server: Boot Target, Networking, and Mounting Drives

Share this:

This NUC started life running Windows, then got Ubuntu Desktop installed with a full GUI, and later got converted to boot headless: no monitor, no keyboard, SSH only. This post covers that conversion: disabling the graphical boot target, what breaks along the way, and re-doing a drive mount that the desktop environment had been handling automatically without me realising it.

The Intel NUC that became a headless Ubuntu server

This is part of a small series on turning this NUC into a proper headless server. The other posts cover setting up reliable Wake-on-LAN with a HomeKit switch and auto-suspend that keeps active transfers and SSH sessions alive.

Ubuntu Desktop is not the same as Server

Ubuntu Desktop and Ubuntu Server aren’t the same OS with or without a GUI bolted on. They ship different default package sets and service stacks entirely. Desktop pulls in NetworkManager for networking, rather than Server’s leaner netplan/systemd-networkd setup. It also brings GDM, a full GNOME session, and a long list of desktop-oriented daemons, including power and session management, that a Server image never installs at all. Disabling the GUI afterward stops it from starting, but none of that underlying software goes away. It just sits there dormant. On hindsight, I should have started fresh with Ubuntu Server rather than keep Desktop-then-convert. But if you do want to convert from Desktop, here’s what needs to be done.

Converting to a headless boot

Before doing this, make sure the machine is genuinely a background server with no monitor or display output anyone actually uses. Disabling the display manager removes any local video output entirely.

Setting the boot target

Set the default boot target to multi-user (text console) instead of graphical:

sudo systemctl set-default multi-user.target

Disable and stop the display manager so it doesn’t start:

sudo systemctl disable gdm3
sudo systemctl stop gdm3

Reboot and confirm:

systemctl get-default
systemctl status gdm3

This should show multi-user.target and gdm3 as inactive/disabled.

Confirming the machine now boots to a text console with GDM disabled

What survives, and what breaks

What survives this untouched: anything running as a system-level systemd service, rather than tied to a GNOME session. SSH, Samba, NetworkManager, WOL settings, and (once set up) autosuspend all keep working exactly as before.

RDP obviously stops working. GNOME’s built-in Remote Desktop has nothing to share once no session is running. It’s not a workable remote-GUI option on a converted box like this. Worth knowing if you were planning to rely on it (xrdp with a virtual display is the usual alternative for headless GUI access).

The login keyring also becomes unreachable, since it normally unlocks automatically at graphical login. If anything you use stores secrets there, it’s effectively locked out on a machine that never logs in graphically again. Audio (PipeWire/WirePlumber) is irrelevant with no session to attach to. GUI-only apps and anything expecting a display will log harmless warnings on startup, but won’t block anything.

Optional: removing the desktop packages

Once you’re confident you don’t want the desktop back, you can remove the packages instead of leaving them installed but dormant:

sudo apt purge ubuntu-desktop gnome-shell gdm3 --autoremove

This is more invasive and harder to reverse. Skipping it is safer if there’s any chance you’ll want the desktop back. Note that sudo systemctl set-default graphical.target undoes the boot-target change instantly, but won’t reinstall anything you’ve purged.

Re-doing a drive mount the desktop was handling silently

The second internal drive on this NUC (/dev/sda1, NTFS, left over from when it ran Windows) had been mounting itself automatically whenever I logged into the GNOME desktop, at a path like /run/media/Media. That’s udisks2 auto-mounting on login, a desktop-session feature. With no GUI session ever starting again, the drive simply never got mounted after converting to headless, since nothing was left to trigger it. This broke the Samba share I had for the volume.

The fix is a proper permanent mount via /etc/fstab, independent of any session. First find the drive’s UUID:

sudo blkid /dev/sda1
Finding the drive UUID and confirming the fstab mount worked

Create the mount point and add an entry to /etc/fstab:

sudo mkdir -p /mnt/storage
sudo nano /etc/fstab

Add a line like this:

UUID=<your-drive-uuid>  /mnt/storage  ntfs3  defaults,nofail,uid=1000,gid=1000  0  0

A few things worth explaining in that line:

  • ntfs3 is the modern in-kernel NTFS driver (mainlined since Linux 5.15), not the older FUSE-based ntfs-3g. It’s faster and doesn’t need a separate userspace package on current kernels. Ubuntu ships it by default.
  • nofail means the system will still boot normally even if this drive isn’t present or fails to mount, rather than dropping to an emergency shell. Worth including on any secondary/removable drive.
  • uid=1000,gid=1000 sets file ownership on mount, since NTFS doesn’t natively carry Unix permissions the way ext4 does. Match these to your actual user’s UID/GID (check with id <username>) so the files are actually writable by your account, rather than only root.

Test it without rebooting:

sudo mount -a
df -h /mnt/storage

If that shows the drive mounted at /mnt/storage, update anything that referenced the old GUI auto-mount path to point at the new fixed path instead. That includes Samba share definitions, download directories, and any scripts. Unlike the old path, this one is stable across reboots and doesn’t depend on anyone ever logging into a desktop session again.

Mounting the server’s Samba share remotely in ForkLift

Sidebar: an unrelated DNS issue on the same box

Not related to the headless conversion itself, but it came up while working on this same NUC’s networking, so it’s worth a note here. At one point sudo apt update started failing with “Temporary failure in name resolution” errors. The cause was a mismatch between the router’s DNS settings and the NUC’s.

The router had DNS-over-TLS turned on for outbound traffic, which encrypts DNS requests going out to the internet. It didn’t support an encrypted handshake on its local private IP address, though.

On the NUC, systemd-resolved was set to require encryption for every DNS lookup, including lookups to the router itself:

DNSOverTLS=yes

The router couldn’t complete a TLS handshake on its local address, so every lookup through it failed. That showed up as apt being unable to reach any repository.

Fix: change the setting from yes to opportunistic.

sudo nano /etc/systemd/resolved.conf
DNSOverTLS=opportunistic
sudo systemctl restart systemd-resolved

With opportunistic, systemd-resolved tries encrypted DNS first. It falls back to plain DNS for that specific server if it can’t negotiate TLS, rather than failing outright. The router still encrypts everything past that local hop.

Next

With the box genuinely headless and storage mounted properly, the next parts of this series cover setting up reliable Wake-on-LAN with a HomeKit switch and auto-suspend that keeps active transfers and SSH sessions alive.



If this post has been useful, support me by buying me a latte or two 🙂
Buy Me A Coffee

Share this:

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.