Run graphical applications on your Azure VMs and display them locally. azlin supports two approaches: VNC for a full remote desktop session and X11 forwarding for lightweight GUI apps.
| Approach | Best For | Latency | Setup |
|---|---|---|---|
| VNC Desktop | Full desktop environment, multiple apps | Higher (full desktop) | Auto-managed |
| VNC Minimal | Window manager only, no desktop overhead | Medium | Auto-managed |
| VNC Single App | One app in VNC (e.g. browser), exits when app closes | Medium | Auto-managed |
| X11 Forwarding | Individual GUI apps (gitk, meld, xeyes) | Low (per-window) | Minimal |
Both approaches work transparently through Azure Bastion tunnels when your VM has no public IP.
WSL2 (Windows):
- WSLg is included with WSL2 by default and provides an X server automatically.
- Verify with:
echo $DISPLAY(should show something like:0)
Linux:
- An X11 display server is already running if you are in a graphical session.
- Verify with:
echo $DISPLAY
macOS:
- Install XQuartz:
brew install --cask xquartz - Log out and back in after installation.
- Enable "Allow connections from network clients" in XQuartz Preferences > Security.
VNC Viewer (for azlin gui only):
azlin guilaunches a localvncviewercommand.- TigerVNC is the tested viewer and provides that binary on Linux, macOS, and Windows/WSL setups.
azlin gui automatically installs any missing VNC/desktop packages on first
use. azlin connect --x11 does not install remote GUI applications or X11
packages for you; it only enables X11 forwarding on the SSH connection.
Launch a full remote desktop session on the VM and view it locally.
# Full XFCE desktop (default)
azlin gui my-vm
# Minimal window manager only (openbox) — no desktop overhead
azlin gui my-vm --minimal
# Single application mode — VNC exits when the app closes
azlin gui my-vm --app "chromium-browser --no-sandbox"
azlin gui my-vm --app "gimp"
# Custom resolution
azlin gui my-vm --resolution 2560x1440
# Specify SSH user and key for setup and tunneling
azlin gui my-vm --user azureuser --key ~/.ssh/azlin_key| Mode | Flag | Desktop | Window Manager | Best For |
|---|---|---|---|---|
| Full Desktop | (default) | XFCE | XFCE WM | Multi-app workflows, full desktop experience |
| Minimal | --minimal |
None | openbox | Lightweight sessions, launch apps from right-click menu |
| Single App | --app "cmd" |
None | None | Running one heavy GUI app (browser, IDE, GIMP) |
Minimal mode starts only the openbox window manager. Right-click on the desktop for an app launcher menu. Drag window edges to resize. Much lighter than a full desktop.
Single app mode runs the specified command directly. The VNC window shows only that application. When the app is closed, the VNC server exits automatically.
Note on Chromium/browsers: Use
--no-sandboxwhen running Chromium in VNC:azlin gui my-vm --app "chromium-browser --no-sandbox". When azlin launches Chromium directly, it auto-wraps snap-backed invocations insystemd-run --user --scope, which avoids the commonis not a snap cgroupfailure on Ubuntu VMs.
- Dependency check: azlin SSHs into the VM with the same
--userand--keysettings you provided and checks for required packages (tigervnc-standalone-server,xfce4,dbus-x11). Missing packages are installed automatically. - VNC server start: A TigerVNC server is started on the VM, bound to
localhostonly (no network exposure). A random password is generated for the session. - Tunnel creation: azlin creates an SSH tunnel (or bastion tunnel) forwarding a local port to the VNC server port on the VM.
- Viewer launch: azlin launches your local VNC viewer, connecting to
localhost:<local_port>with the session password. - Cleanup: When you close the VNC viewer, azlin stops the VNC server on the VM and tears down the tunnel.
| Option | Default | Description |
|---|---|---|
--resolution |
1920x1080 |
Desktop resolution (WIDTHxHEIGHT) |
--depth |
24 |
VNC color depth (8, 16, or 24) |
--user |
azureuser |
SSH username on the VM |
--key |
~/.ssh/azlin_key |
Path to SSH private key |
--minimal |
false | Use openbox window manager instead of full XFCE desktop |
--app |
none | Run a single application (e.g. --app "chromium-browser --no-sandbox") |
-y, --yes |
false | Compatibility flag; GUI dependency setup is already non-interactive |
azlin automatically detects and installs missing packages on first use. The following packages are managed:
| Package | Purpose |
|---|---|
tigervnc-standalone-server |
VNC server |
xfce4 |
Lightweight desktop environment |
xfce4-terminal |
Terminal emulator for the desktop |
dbus-x11 |
D-Bus session bus (required by XFCE) |
Installation happens once per VM and takes 2-3 minutes. Subsequent connections
skip this step. The package setup is non-interactive; --yes is accepted for
CLI compatibility but does not change the behavior. If setup cannot finish
cleanly, azlin gui exits with the setup error instead of waiting indefinitely.
VNC security is handled through multiple layers:
- Localhost binding: The VNC server listens on
127.0.0.1only. It is never exposed to the network. - Random passwords: A unique password is generated for each session using
openssl rand. Passwords are not stored on disk. - SSH tunnel: All VNC traffic travels through the encrypted SSH (or bastion) tunnel. No VNC traffic crosses the network unencrypted.
- Automatic cleanup: The VNC server is stopped when the session ends, leaving no listening services behind.
Connection refused when viewer launches
The tunnel may not be ready yet. azlin waits for the tunnel to be established before launching the viewer, but on slow connections this can race:
# Retry the connection
azlin gui my-vmGUI dependency/setup phase failed (exit 1): exit code 1
This empty setup error usually means the local azlin client is too old and is packaging the remote GUI dependency check incorrectly, especially on private VMs that route through Azure Bastion. Upgrade or rebuild azlin, then retry:
azlin gui my-vmIf the error persists after upgrading, verify that basic SSH still works first:
azlin connect my-vm --no-tmux -- whoamiBlack screen or no desktop
The desktop environment may not have started correctly:
# SSH into the VM and check the VNC log
azlin connect my-vm
cat ~/.vnc/*.log
# Restart the desktop environment
vncserver -kill :1
vncserver -localhost yes -geometry 1920x1080 :1Screen resolution is wrong
Pass the --resolution flag:
azlin gui my-vm --resolution 2560x1440Supported resolutions: any valid WIDTHxHEIGHT value (e.g., 1280x720, 1920x1080, 2560x1440, 3840x2160).
Clipboard not working between local and remote
Install autocutsel on the VM for bidirectional clipboard support:
# On the VM
sudo apt-get install -y autocutsel
autocutsel -forkForward individual GUI windows from the VM to your local display. Best for lightweight apps where you don't need a full desktop.
# Connect with X11 forwarding enabled
azlin connect --x11 my-vm
# Then on the VM, run any GUI app:
xeyes &
gitk --all &
meld file1 file2 &azlin connect --x11adds the-Yflag (trusted X11 forwarding) to the SSH connection.- SSH sets up an encrypted tunnel for X11 protocol traffic.
- The remote
DISPLAYenvironment variable is set automatically by SSH. - GUI windows render on your local X server through the tunnel.
- When connecting through Azure Bastion, the X11 tunnel is layered on top of the bastion tunnel seamlessly.
You can run any remote GUI app directly without opening an interactive session:
# Run a single app via X11 — app window appears locally
azlin connect my-vm --x11 --no-tmux -- chromium-browser --no-sandbox
azlin connect my-vm --x11 --no-tmux -- eog ~/screenshot.png
azlin connect my-vm --x11 --no-tmux -- thunar
azlin connect my-vm --x11 --no-tmux -- gitk --all
azlin connect my-vm --x11 --no-tmux -- meld file1.py file2.pyThe --no-tmux flag avoids wrapping in tmux, and -- separates azlin args from the remote command. The app renders locally and the connection closes when the app exits.
If you open an interactive X11 shell with azlin connect --x11 my-vm and then launch Chromium manually on an older VM, use systemd-run --user --scope chromium-browser --no-sandbox inside that shell. Newly provisioned azlin VMs install /usr/local/bin/chromium-browser and /usr/local/bin/chromium wrappers that add the required user-systemd scope automatically.
| Application | Command | Purpose |
|---|---|---|
| xeyes | xeyes |
Quick test that X11 forwarding works |
| gitk | gitk --all |
Visual git history browser |
| meld | meld dir1 dir2 |
Visual diff and merge tool |
| gedit | gedit file.py |
Lightweight text editor |
| Chromium | chromium-browser --no-sandbox |
Web browser (consider VNC for better performance) |
| eog | eog image.png |
Image viewer |
| thunar | thunar |
File manager |
| Firefox | firefox |
Web browser (heavier, consider VNC) |
| VS Code | code --disable-gpu |
Editor (use --disable-gpu over SSH) |
Error: Can't open display
The DISPLAY variable is not set on the VM. This usually means X11 forwarding was not enabled.
# Verify the connection was made with --x11
azlin connect --x11 my-vm
# On the VM, check DISPLAY is set
echo $DISPLAY
# Should show something like: localhost:10.0X11 connection rejected because of wrong authentication
xauth cookies are mismatched. Regenerate them:
# On the VM
xauth generate $DISPLAY . trustedWarning: No xauth data
The xauth package may be missing on the VM:
# On the VM
sudo apt-get install -y xauth
# Disconnect and reconnect with --x11Apps are slow or laggy
X11 forwarding sends individual draw commands over the network, which can be slow for complex UIs. Options:
- Use
ssh -C(compression) if you need it on a manual SSH command. - For heavy GUI usage, switch to VNC (
azlin gui) which sends compressed screen updates instead.
Both X11 and VNC work through Azure Bastion tunnels. If connections fail when using Bastion:
# Verify Bastion tunnel is working
azlin bastion status my-bastion --resource-group my-rg
# Test basic SSH connectivity first
azlin connect my-vm
# Then try GUI forwarding
azlin connect --x11 my-vmNo additional firewall or NSG rules are needed. Both X11 and VNC traffic travels inside the SSH tunnel, which uses only port 22 (or the bastion tunnel).
- VNC: Best for multi-app workflows or desktop environments. Compress traffic by choosing a reasonable resolution.
- X11: Best for lightweight apps (gitk, meld, xeyes). Avoid full browsers or IDEs.
- Region proximity: VMs in regions closer to you will have noticeably lower GUI latency.
- VM size: GUI rendering uses CPU; choose at least
Standard_D2s_v3or above for a smooth experience.