Converting Ubuntu Desktop to a Headless Server: Boot Target, Networking, and Mounting Drives
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.

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.

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

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:
ntfs3is the modern in-kernel NTFS driver (mainlined since Linux 5.15), not the older FUSE-basedntfs-3g. It’s faster and doesn’t need a separate userspace package on current kernels. Ubuntu ships it by default.nofailmeans 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=1000sets 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 withid <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.

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 🙂
