Operating Systems Azure

Ubuntu 22.04 LTS with GNOME Desktop on Azure User Guide

| Product: Ubuntu 22.04 LTS with GNOME Desktop on Azure

Overview

This guide covers the deployment and configuration of Ubuntu 22.04 LTS with the GNOME desktop on Microsoft Azure using cloudimg's pre configured virtual machine image from the Azure Marketplace.

Ubuntu 22.04 LTS, codename Jammy Jellyfish, is Canonical's long term support release. This image is the genuine Ubuntu Server base with the distribution's own desktop added: ubuntu-desktop-minimal, installed without its recommended extras so the machine stays server shaped. You reach the desktop with any VNC viewer through TigerVNC, and each user gets their own full GNOME session running on the virtual machine.

The image is built so that a freshly deployed machine exposes nothing but SSH. VNC ships switched off, no account has a password, no VNC password exists anywhere in the image, and when you do turn VNC on the server listens on localhost only, so you reach it through an SSH tunnel rather than over the internet.

What's included:

  • Ubuntu 22.04.5 LTS on the 6.8 Azure kernel, with every available update applied at build time, phased updates included
  • The GNOME desktop from the Ubuntu main component: GNOME Shell 42.9, the GDM 42 login screen and mutter 42.9
  • Desktop applications from main, including Files (Nautilus), Terminal, Text Editor, System Monitor, Settings and Disks
  • TigerVNC 1.12 (tigervnc-standalone-server), configured for per user desktops on localhost only, with every offered security type requiring the VNC password
  • cloudimg-vnc, a small helper that shows the VNC state and starts or stops a user's desktop in one command
  • Guest sleep disabled, so the desktop never offers a Suspend that would leave an Azure virtual machine unreachable
  • unattended-upgrades armed, so the machine keeps applying security updates by itself
  • AppArmor enforcing and the Azure provisioning path unchanged (cloud-init, the Azure Linux Agent, systemd-networkd)
  • Gen2 Hyper V virtual machine support
  • 24/7 cloudimg support

The image carries around 1,180 packages and uses about 6 GiB of the 30 GiB operating system disk, leaving the rest for your own software and data.

Platform: Microsoft Azure (Gen2 Hyper V, x64) Default user: azureuser

Why VNC, and not RDP

On Ubuntu 22.04 the two obvious remote desktop options do not fit a cloud image that must stay fully patched without a paid subscription:

  • xrdp is in Ubuntu's universe component, and its security fixes on 22.04 are published only through Ubuntu Pro's Expanded Security Maintenance. An image built on it could not be kept current without a Pro subscription. cloudimg does not ship it, and this image asserts its absence at build time and again on every machine.
  • GNOME Remote Desktop on this release is 42, which only shares a session that someone is already logged into at the machine. Headless remote login over RDP arrived in GNOME 46. The package is present because the desktop depends on it, but it is never enabled and holds no credentials.

TigerVNC is the option that is both first party and fully maintained on 22.04. Its one security fix for this release (CVE-2023-1393) shipped in the ordinary security pocket, in exactly the version this image carries, so the machine can keep itself patched from the free archive. Every package in the image is covered by ordinary Ubuntu security updates: none of them needs Ubuntu Pro.

VNC's own password protocol is old: it uses only the first 8 characters of the password. That is why this image never exposes VNC to the network. The policy file /etc/tigervnc/vncserver-config-mandatory forces two settings for every user, and TigerVNC loads it after each user's own configuration and after the command line, so a user's settings cannot undo it:

  • $localhost = "yes"; makes the VNC server listen on 127.0.0.1 and ::1 only. You connect through an SSH tunnel, which is encrypted and authenticated with your SSH key.
  • $SecurityTypes = "TLSVnc,VncAuth"; offers only security types that require the VNC password. None (no password) is never offered.

GDM runs with Wayland disabled (WaylandEnable=false in /etc/gdm3/custom.conf) so that the login screen and the VNC sessions are both on X11.

The two passwords, and which one goes where

Neither password exists until you create it, and both belong to you:

  1. Your VNC password. You set it yourself by running vncpasswd as your own user, without sudo. On TigerVNC 1.12 it is stored at ~/.vnc/passwd, readable only by you. Your VNC viewer asks for it when it connects. Only the first 8 characters count.
  2. Your account password. You set it yourself with sudo passwd <your-user>. The VNC session logs you straight into GNOME, but the desktop still asks for this password when the screen locks and when an application needs administrator rights. The image ships with no account password at all.

SSH stays key only throughout. Setting either password does not enable password logins over SSH.

Security posture

This image ships with no known credential:

  • No account has a password. The root account is locked and your administrator account is created at first boot with only the SSH key you supply.
  • SSH is key only (PasswordAuthentication no).
  • No VNC password exists for any account, no VNC desktop is configured, and a new virtual machine listens on SSH port 22 only. Port 22 is the only port the listing advertises.
  • When VNC is on, it listens on localhost only and every offered security type requires the VNC password.
  • The helper refuses to start a desktop for a user who has no account password or no VNC password.
  • Guest sleep is disabled, so the desktop menu offers Restart, Power Off and Log Out but never Suspend.
  • SSH host keys are removed before capture and regenerated on each machine at first boot, and /etc/machine-id is regenerated per machine, so no two machines share a host identity. The login screen's own PulseAudio cookie is removed before capture and is recreated per machine.
  • AppArmor is enforcing and GDM automatic login is not enabled.
  • The network renderer is pinned to systemd-networkd (see "Important Notes") and NetworkManager is not installed.

Known issue: TigerVNC and CVE-2026-34352

CVE-2026-34352 does not apply to this image, and the reason is worth stating precisely. Checked on 18 September 2026 against Ubuntu's security tracker:

  • The CVE is against x0vncserver, TigerVNC's screen scraping server, whose Image.cxx uses incorrect permissions so that other local users can observe or manipulate screen contents. Ubuntu lists it for 22.04 as needs-triage, priority medium.
  • On Ubuntu, x0vncserver is packaged in tigervnc-scraping-server, which this image does not install. The binary is not on the machine at all, which you can check yourself with the commands in Step 11.
  • The remote desktop this image ships is Xvnc, a virtual X server from tigervnc-standalone-server. It is a different program and is not the subject of that CVE.

Support window

Ubuntu 22.04 LTS receives standard security maintenance until 1 June 2027. That is the date the free archive stops publishing updates for it. After that, continued security updates require an Ubuntu Pro subscription, which this image does not use and does not need before that date. Plan a move to Ubuntu 24.04 LTS ahead of it. cloudimg publishes a 24.04 LTS desktop image on the same design.

Prerequisites

Before deploying this image, ensure you have:

  1. An active Microsoft Azure subscription
  2. Access to the Azure Portal or Azure CLI
  3. An SSH key pair for Linux VM access
  4. A VNC viewer: TigerVNC Viewer (Windows, macOS, Linux), RealVNC Viewer, Remmina on Linux, or the built in Screen Sharing app on macOS

Recommended VM Size: Standard_B2ms (2 vCPU, 8 GB RAM) or larger. Measured on this image: the login screen alone uses about 400 MiB of memory and one idle GNOME VNC session brings the total to roughly 900 MiB, so Standard_B2ms leaves comfortable room for a browser and a few applications. There is no GPU requirement: the desktop renders in software.

Step 1: Deploy the Virtual Machine

Option A: Azure Portal

  1. Navigate to the Azure Marketplace and search for "Ubuntu 22.04 LTS with GNOME Desktop cloudimg"
  2. Select the image and click Create
  3. Configure the basics:
  4. Subscription: Select your Azure subscription
  5. Resource Group: Create new or select existing
  6. Virtual Machine Name: Enter a name for your VM
  7. Region: Select your preferred Azure region
  8. Size: Standard_B2ms or larger
  9. Under Administrator Account, select SSH public key and enter your key
  10. Under Inbound Port Rules, allow SSH (port 22) only. Do not open any VNC port: the desktop is reached through an SSH tunnel
  11. Click Review + Create, then Create

Option B: Azure CLI

Run this on your own workstation, substituting your resource group, VM name and preferred region. Accept the Marketplace terms once per subscription, then create the machine:

az vm image terms accept --urn cloudimg1647283583153:ubuntu-22-04-gui:default:latest

az vm create \
  --resource-group myResourceGroup \
  --name my-ubuntu-desktop \
  --image cloudimg1647283583153:ubuntu-22-04-gui:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Step 2: Connect via SSH

Run these on your own workstation, not on the VM. Find the public IP, then connect:

az vm show --resource-group myResourceGroup --name my-ubuntu-desktop --show-details --query publicIps -o tsv
ssh azureuser@<vm-ip>

There is no password to enter. If SSH asks you for one, the key you supplied at create time is not the key your client is offering.

Step 3: Confirm the release and the desktop

Check that the machine is Ubuntu 22.04, that it boots to the graphical target, that the GNOME login screen (GDM) is running with Wayland disabled, and which desktop and VNC packages are installed:

lsb_release -ds
uname -srm
systemctl get-default
systemctl is-active gdm.service
grep '^WaylandEnable' /etc/gdm3/custom.conf
dpkg-query -W -f='${Package} ${Version}\n' ubuntu-desktop-minimal gdm3 gnome-shell mutter-common xserver-xorg-core tigervnc-standalone-server

The terminal shows Ubuntu 22.04.5 LTS on kernel 6.8.0-1067-azure, the default target graphical.target with GDM active, WaylandEnable=false, and the ubuntu-desktop-minimal, gdm3, gnome-shell, mutter, Xorg and tigervnc package versions

Expected output:

Ubuntu 22.04.5 LTS
Linux 6.8.0-1067-azure x86_64
graphical.target
active
WaylandEnable=false
ubuntu-desktop-minimal 1.481.5
gdm3 42.0-1ubuntu7.22.04.4
gnome-shell 42.9-0ubuntu2.3
mutter-common 42.9-0ubuntu9
xserver-xorg-core 2:21.1.4-2ubuntu1.7~22.04.16
tigervnc-standalone-server 1.12.0+dfsg-4ubuntu0.22.04.1

The desktop comes from Ubuntu's main component and TigerVNC from universe, both from Canonical's own archive. Nothing comes from a third party repository:

echo "packages installed: $(dpkg-query -W -f='${Package}\n' | wc -l)"
apt-cache policy ubuntu-desktop-minimal | sed -n '4,6p'
apt-cache policy tigervnc-standalone-server | sed -n '4,6p'
df -h /

Step 4: Check that VNC is off

A new virtual machine has VNC switched off. The helper shows the whole picture in one command:

sudo cloudimg-vnc status

The terminal shows cloudimg-vnc status reporting the localhost only policy, no VNC desktops configured, nothing listening on the VNC ports and no user with a VNC password, followed by the TCP listeners limited to SSH on port 22

Expected output on a new VM:

VNC policy: localhost only, security types TLSVnc,VncAuth (/etc/tigervnc/vncserver-config-mandatory)
VNC desktops configured: none
listening on VNC ports 5900-5999: none
users with a VNC password set: none (each user runs: vncpasswd)

The helper lives at /usr/local/sbin/cloudimg-vnc, with /usr/sbin/cloudimg-vnc as a symlink so that sudo cloudimg-vnc works whatever sudo's secure_path happens to be. Its subcommands are status, enable [<user>], disable [<user>] and details; <user> defaults to your administrator account.

Confirm the policy and the network side as well:

grep -vE '^#|^$' /etc/tigervnc/vncserver-config-mandatory
sudo ss -tln | awk '$1=="LISTEN"{print $4}' | sort
echo "off-box listeners: $(sudo ss -tln | awk '$1=="LISTEN"{print $4}' | grep -vcE '^(127\.|\[::1\])' || true)"

This VM's own first boot details are readable by root:

sudo cloudimg-vnc details

There is no password in that file, by design: TigerVNC has no per machine server secret, and both of the secrets involved are yours to choose.

Step 5: Set your two passwords

Set your account password first. The desktop asks for it when the screen locks and when an application needs administrator rights:

sudo passwd azureuser

Then set your VNC password. Run this without sudo, as your own user, because it writes into your own home directory:

vncpasswd

Answer n when it offers a view only password unless you want one. Only the first 8 characters of a VNC password are significant.

Check that it was written, readable only by you. (Resolving the path from the first boot details file works whether you run this as yourself or with sudo.)

VNCUSER="$(sudo awk -F= '/^VNC_USERNAME=/{print $2}' /root/cloudimg-vnc-details.txt)"
sudo ls -l "$(getent passwd "$VNCUSER" | cut -d: -f6)/.vnc/passwd"

Step 6: Start your VNC desktop

sudo cloudimg-vnc enable azureuser

The helper picks the first free display, adds the mapping to /etc/tigervnc/vncserver.users, starts tigervncserver@:1.service, waits for the port and then checks that the port really is bound to localhost only. If it is not, it stops the desktop again and tells you.

Expected output:

VNC desktop for azureuser enabled on display :1: listening on localhost port 5901 only
from your own machine: ssh -N -L 5901:localhost:5901 azureuser@<vm-ip>
then point a VNC viewer at localhost:5901 and enter azureuser's VNC password

Check what is now running:

sudo cloudimg-vnc status
sudo ss -tln | awk '$1=="LISTEN"{print $4}' | grep 5901 || echo "no VNC desktop running"

Step 7: Connect through an SSH tunnel

Run this on your own workstation, not on the VM. It opens a tunnel and stays in the foreground; leave it running.

ssh -N -L 5901:localhost:5901 -o ServerAliveInterval=60 azureuser@<vm-ip>

The ServerAliveInterval matters: an idle SSH tunnel to an Azure VM is dropped after about four minutes without it.

Now point your VNC viewer at localhost:5901 and enter your VNC password. TigerVNC Viewer will warn that the certificate is not signed by a known authority; that is expected, because the connection uses anonymous TLS inside a tunnel that is already authenticated by your SSH key.

The TigerVNC viewer connected through the SSH tunnel shows the Ubuntu GNOME desktop with the dock on the left and the top bar

Step 8: Use the desktop

The VNC session logs you straight into GNOME as your own user, at 1920x1080 by default. The dock is on the left and the Activities overview is in the top left corner.

To change the resolution, create ~/.vnc/tigervnc.conf with your own geometry and restart the desktop:

mkdir -p ~/.vnc
printf '$geometry = "2560x1440";\n1;\n' | tee ~/.vnc/tigervnc.conf
sudo cloudimg-vnc disable azureuser
sudo cloudimg-vnc enable azureuser

The localhost and security type settings cannot be changed this way: the mandatory policy file is loaded after your configuration and overrides both.

Step 9: Desktops for more users

Each user gets their own desktop on their own display and port. Create the account, give it a password, let that user set their own VNC password, then enable it:

sudo adduser alice
sudo passwd alice
sudo -u alice -i vncpasswd
sudo cloudimg-vnc enable alice

alice gets display :2 on port 5902. Tunnel to that port the same way.

Step 10: Turn VNC off again

sudo cloudimg-vnc disable azureuser

This stops the session, removes the mapping and the boot time enablement, and confirms that nothing is listening on the port any more. Your passwords are untouched, so turning it back on is one command.

Step 11: Verify the security posture

These checks are the ones cloudimg runs on every build, and they are worth running yourself on a new machine.

No account has a usable password before you set one, and the root account is locked:

sudo awk -F: '{h=$2; sub(/^!+/,"",h); if (h ~ /^\$[0-9a-z]+\$/) print $1}' /etc/shadow | sed 's/^/usable password: /' || true
echo "accounts with a usable password: $(sudo awk -F: '{h=$2; sub(/^!+/,"",h); if (h ~ /^\$[0-9a-z]+\$/) print $1}' /etc/shadow | grep -c . || true)"
sudo passwd -S root

SSH is key only:

sudo sshd -T | grep -E '^(passwordauthentication|kbdinteractiveauthentication|permitrootlogin) '

Nothing but SSH is reachable from off the machine:

echo "off-box TCP ports: $(sudo ss -tln | awk '$1=="LISTEN"{print $4}' | grep -vE '^(127\.[0-9.]+|::1|\[::1\])(%[a-z0-9]+)?:' | sed -E 's/.*:([0-9]+)$/\1/' | sort -un | tr '\n' ' ')"

The screen scraping server that CVE-2026-34352 is about is not installed:

echo "tigervnc-scraping-server installed: $(dpkg -s tigervnc-scraping-server >/dev/null 2>&1 && echo yes || echo no)"
echo "x0vncserver present: $([ -e /usr/bin/x0vncserver ] && echo yes || echo no)"

xrdp, whose 22.04 fixes are Ubuntu Pro only, is not installed:

echo "xrdp installed: $(dpkg -s xrdp >/dev/null 2>&1 && echo yes || echo no)"

No installed package needs an Ubuntu Pro subscription to stay patched. This is the check that matters most on a 22.04 image, and apt list --upgradable does not answer it, because Pro only fixes never appear there:

sudo pro security-status --format json | python3 -c 'import json,sys; s=json.load(sys.stdin)["summary"]; print("packages needing Ubuntu Pro (esm-infra):", s["num_esm_infra_updates"]); print("packages needing Ubuntu Pro (esm-apps):", s["num_esm_apps_updates"]); print("universe packages installed:", s["num_universe_packages"]); print("main packages installed:", s["num_main_packages"])'

The terminal shows pro security-status reporting zero packages needing Ubuntu Pro for either esm-infra or esm-apps, with six universe packages and the rest from main, followed by the off-box TCP port list containing only 22

Expected output:

packages needing Ubuntu Pro (esm-infra): 0
packages needing Ubuntu Pro (esm-apps): 0
universe packages installed: 6
main packages installed: 1174

The six universe packages are TigerVNC's three, one Perl module TigerVNC uses, and two that Ubuntu Server itself already carried. Everything else, the whole desktop included, is from main.

The desktop cannot suspend the machine:

busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanSuspend
busctl call org.freedesktop.login1 /org/freedesktop/login1 org.freedesktop.login1.Manager CanPowerOff

CanSuspend answers na and CanPowerOff answers yes.

Step 12: Patching and updates

The machine applies security updates by itself:

systemctl is-enabled unattended-upgrades.service
apt-config dump | grep -E 'APT::Periodic::(Update-Package-Lists|Unattended-Upgrade)'

To check and apply updates by hand:

sudo apt-get update
apt list --upgradable 2>/dev/null | tail -n +2 | wc -l
sudo apt-get -y dist-upgrade

A kernel update needs a restart, which ends any VNC session. Restart from SSH, then start your desktop again:

[ -f /var/run/reboot-required ] && echo "restart required" || echo "no restart required"

Desktop applications you add yourself follow the same route. Firefox is deliberately not pre installed, because on Ubuntu it is a snap and installing it at build time would bake this machine's snap identity into every copy of the image. Install it on your own machine when you want it:

sudo snap install firefox

Step 13: Networking, the firewall, and services that ship switched off

The image ships no firewall rules of its own: your Azure Network Security Group is the boundary, and it should allow port 22 only. There is no reason to open a VNC port, and doing so would expose a protocol whose password is truncated to 8 characters.

Printing and network discovery daemons are not installed on this profile, so there is nothing listening for them. Should you want them, install them in the ordinary way and remember that they add listening ports:

echo "cups-daemon installed: $(dpkg -s cups-daemon >/dev/null 2>&1 && echo yes || echo no)"
echo "avahi-daemon installed: $(dpkg -s avahi-daemon >/dev/null 2>&1 && echo yes || echo no)"

The network is managed by systemd-networkd, pinned by /etc/netplan/00-network-manager-all.yaml:

netplan get network.renderer
networkctl status eth0 | grep -E 'State:|Online state:' || true

Verify what you are running

lsb_release -ds
systemctl get-default
systemctl is-active gdm.service graphical.target
sudo cloudimg-vnc status
echo "failed units: $(systemctl --failed --no-legend | wc -l)"

Common Use Cases

  • Cloud workstation. A full GNOME desktop on a long term support Ubuntu release, close to your cloud resources, reached only through an SSH tunnel.
  • Graphical administration. Database and cloud consoles, diagramming, and tools that have no command line equivalent.
  • Training and lab desktops. One desktop per user on its own display and port, each with its own two passwords.
  • Secure jump desktop. A machine that exposes nothing but SSH, from which you reach the rest of a private network.

Troubleshooting

"Connection refused" on localhost:5901

The tunnel is not up, or the desktop is not running. Check sudo cloudimg-vnc status on the VM, and check that the ssh -N -L command on your workstation is still in the foreground and has not exited.

The viewer says "Authentication failure"

The VNC password is wrong. Remember that only the first 8 characters count, so two passwords sharing their first 8 characters are the same password. Reset it with vncpasswd as your own user.

Connecting directly to the VM's public IP on port 5901 does not work

That is the design. The VNC server binds 127.0.0.1 and ::1 only, and the policy that enforces that is loaded after every user configuration. Use the SSH tunnel.

The tunnel drops after a few minutes

Azure closes idle connections at about four minutes. Add -o ServerAliveInterval=60 to the ssh -N -L command.

The screen is locked and asks for a password

That is your account password, not your VNC password. If you have not set one, open a second SSH session and run sudo passwd <your-user>.

The desktop does not start for a user

cloudimg-vnc enable refuses when the account has no password or the user has no VNC password, and says which. If both are set and it still fails, read journalctl -u tigervncserver@:1 and the log in that user's ~/.vnc/.

"This connection is not secure" in TigerVNC Viewer

Expected. The VNC layer uses anonymous TLS, so there is no certificate to verify, and the connection is already inside an SSH tunnel authenticated with your key.

Cannot connect via SSH

Check the NSG allows port 22 from your address, that the VM is running, and that your client is offering the key you supplied at create time. There is no password login to fall back on.

Important Notes

  • Do not delete /etc/netplan/00-network-manager-all.yaml. The Ubuntu desktop metapackage depends on ubuntu-settings, which declares renderer: NetworkManager for the whole netplan configuration. This image does not install NetworkManager, so that file in /etc shadows it and keeps the network on systemd-networkd. Remove it and the machine loses its network on the next restart.
  • Ubuntu 22.04 LTS receives free security updates until 1 June 2027. Plan a move to 24.04 LTS before then.
  • Only the first 8 characters of a VNC password are significant.
  • Firefox is not pre installed on purpose; install the snap yourself when you want it.
  • The image ships with no account password and no VNC password. Both are yours to set.

Support

cloudimg provides 24/7 support for this image. Contact support through the Azure Marketplace listing or at cloudimg.co.uk.