17 June 2026

A throwaway Linux browsing desktop in an LXC over RDP (and why I gave up on Chrome)

I wanted a disposable little Linux desktop I could reach from a Windows machine over RDP, purely for web browsing — the sort of thing you spin up, point at the internet, and don't much care about. It lives as an unprivileged LXC container on one of my Proxmox boxes. "How hard can it be?" I thought, which as ever should have been my first warning.

It turned out to be straightforward in every respect bar one — the browser — and the browser is rather the point. So this is partly a build note and partly a cautionary tale about Chrome and containers.

The plan

  • An unprivileged LXC (Ubuntu 24.04) on Proxmox, on the LAN via DHCP.
  • A lightweight desktop, reachable over RDP from Windows' built-in Remote Desktop client.
  • A browser. I reached for Google Chrome out of habit.

Unprivileged is the right default here — it's a browser box I'd rather not hand the host's keys to. Keep that word "unprivileged" in mind, because it's where the trouble lives.

Getting the desktop up

The container itself is the easy bit:

pct create 9006 local:vztmpl/ubuntu-24.04-standard_24.04-2_amd64.tar.zst \
  --hostname penny-desktop --cores 4 --memory 4096 --swap 512 \
  --rootfs local-lvm:30 --net0 name=eth0,bridge=vmbr0,ip=dhcp,firewall=1 \
  --unprivileged 1 --features nesting=1

(nesting=1 is worth having — it lets the in-container systemd and a few desktop bits behave themselves.)

Then a desktop and an RDP server. I started with XFCE and xrdp, which is the classic pairing. Here's the first gotcha, and it's a quiet one: on a minimal Ubuntu 24.04 you also need the Xorg backend for xrdp explicitly, or sessions die the instant you connect with a blank grey screen and nothing else:

apt-get install -y xorg xorgxrdp

Without that, /usr/lib/xorg/Xorg simply isn't present, xrdp can't start an X server, and the session collapses. The sesman log spells it out if you go looking — Error starting X server on display 10 — but the symptom from the Windows end is just an empty desktop with no panel and no right-click menu, which is maddeningly uninformative.

A small XFCE annoyance worth knowing about

XFCE 4.18 (which is what 24.04 ships) won't run a .desktop launcher off the desktop until you mark it "trusted", and the only blessed way to set that flag is GIO metadata — which needs a running gvfs daemon that the minimal image doesn't have. So your nice Chrome icon sits there doing absolutely nothing when you click it. You can work around it with a plain executable shell script instead, but at this point I decided XFCE wasn't earning its keep and switched to MATE, which behaves like a Windows user expects (taskbar at the bottom, launchers that just launch) and has none of the trust faff. caja, MATE's file manager, wants ~/.config/caja to exist and be writable — create it and chown it to your user or you'll get a grumble on login.

So far, so fixable. And then the actual browser.

The part where Chrome quietly refuses to work

Chrome installed fine. It launched fine. And then every single page reported the machine was offline — "This site can't be reached", the lot — despite the container having perfectly good internet. This is the bit that cost me the evening.

The maddening thing is that the container itself was demonstrably online:

ping -c2 google.com      # fine, 8ms
curl -sI https://google.com   # HTTP/2 200, no problem at all

Both as root and as the desktop user. DNS resolved, TLS handshook, the lot. But Chrome — and only Chrome — was convinced it had no network. A headless --dump-dom confirmed it: the page came back with class="offline", Chrome's internal "there is no internet" state, not a TLS error or a DNS error. It wasn't failing to load a page; it had decided the network didn't exist.

I did what one does. I threw flags at it:

  • --no-sandbox — the usual incantation for Chrome in a container.
  • --disable-setuid-sandbox
  • --disable-dev-shm-usage — in case /dev/shm was too small (it wasn't, it was 63GB).
  • --disable-features=NetworkServiceSandbox

None of it helped. I even went as far as setting the container's AppArmor profile to unconfined, which did look like it might be heading somewhere — but that's a real security downgrade on a box whose entire job is to talk to the open internet, and that's precisely the wrong direction. The moment I typed it I knew I was bodging.

Stepping back

Here's the thing I should have seen sooner. Every single problem in that last hour traced to one cause: Chrome is about the hardest browser there is to run inside an unprivileged LXC.

Chrome isn't one process. It's a browser process, a GPU process, a network service process, and a renderer per tab — and each runs under its own seccomp-bpf syscall filter. That's lovely for security on a normal desktop and a genuine nuisance inside a container that also applies a seccomp filter. The two filters interact, and the network service ends up unable to make the syscalls it needs. And — this is the crucial bit — --no-sandbox only relaxes the renderer sandbox. The network service keeps its own, which is exactly why connectivity stayed broken no matter how many flags I added. I was relaxing the wrong sandbox.

You can beat it into submission with a privileged container or an unconfined AppArmor profile, but then you've thrown away the very thing (unprivileged isolation) that made this a sensible idea. For a turnkey browsing box that's a bad trade.

The fix: stop using Chrome

Firefox has none of this. Its process model is lighter, its sandbox degrades gracefully rather than refusing to network, and it runs in an unprivileged LXC with no flags, no AppArmor downgrade, no /dev/shm trickery — nothing. It just works.

One wrinkle: don't take Ubuntu's firefox package, because on 24.04 it's a transitional stub that installs Firefox as a snap, and snapd's own confinement is its own special headache inside an unprivileged container. Pull the real .deb straight from Mozilla and pin apt to prefer it:

install -d -m 0755 /etc/apt/keyrings
wget -q https://packages.mozilla.org/apt/repo-signing-key.gpg \
  -O /etc/apt/keyrings/packages.mozilla.org.asc
echo "deb [signed-by=/etc/apt/keyrings/packages.mozilla.org.asc] https://packages.mozilla.org/apt mozilla main" \
  > /etc/apt/sources.list.d/mozilla.list
printf 'Package: *\nPin: origin packages.mozilla.org\nPin-Priority: 1000\n' \
  > /etc/apt/preferences.d/mozilla
apt-get update && apt-get install -y firefox

I then reverted the AppArmor change (back to a clean, ordinary unprivileged container), removed Chrome entirely, and dropped Firefox's stock .desktop file onto the desktop — which, this being MATE, launches when you click it like a civilised thing.

To be sure I wasn't handing over another dud, I took a headless screenshot from inside the container before declaring victory:

firefox --headless --screenshot /tmp/test.png https://www.google.com

— and there was Google's cookie-consent page, in English, served from the UK. Online. Et voilà.

In short

Symptom Cause Fix
Blank grey desktop on RDP connect xrdp has no Xorg backend apt install xorg xorgxrdp
Desktop icon won't launch (XFCE) 4.18 "trusted launcher" + no gvfs Use MATE, or a plain executable script
caja complains on login ~/.config/caja missing/unwritable create it, chown to the user
Chrome says "offline" though curl works network-service seccomp sandbox vs LXC use Firefox

The real lesson isn't about any one flag — it's that if you find yourself disabling security layer after security layer to make a tool work, the tool is probably the wrong one. Chrome is magnificent on a real desktop and a poor houseguest in an unprivileged container. Firefox is the turnkey answer here, and the box has been quietly behaving itself ever since.

I hope this saves someone an evening. Ta ta for now.

No comments:

Post a Comment