TechSetupGuides
Intermediateproxmoxubuntucloud-initxrdpandroidkvmnested-virtualization

Dev Sandbox VM — Proxmox Ubuntu 24.04 with xrdp

Set up a Proxmox VM running Ubuntu 24.04 LTS cloud image with xrdp remote desktop, Android development tools, and nested virtualization. Fully automated via cloud-init with manual post-boot configuration for lightdm and Android SDK.

  1. Step 1

    Overview

    This guide documents the complete setup of a development sandbox VM on Proxmox VE using Ubuntu 24.04 LTS cloud image. The VM provides a full desktop environment via xrdp, Android development tools, and nested virtualization for running VMs inside the VM.

    Key Features:

    • Remote desktop access via xrdp (port 3389)
    • SSH access (port 22, key + password auth)
    • XFCE4 desktop environment
    • Android SDK and development tools
    • Nested KVM virtualization
    • Automated setup via cloud-init

    VM Configuration:

    • VM ID: 102
    • Hostname: dev-sandbox
    • IP: 192.168.1.xxx (assigned by DHCP; find yours via Proxmox Summary or router)
    • OS: Ubuntu 24.04 LTS (cloud image)
    • User: youruser
    • Memory: 8192 MB
    • Cores: 4
  2. Step 2

    Download Ubuntu Cloud Image

    Download the Ubuntu 24.04 LTS cloud image to Proxmox local storage. This image is pre-configured for cloud-init and optimized for virtualization.

    wget -O /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img \
      https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
  3. Step 3

    Create Cloud-Init User Data File

    Create the cloud-init user data YAML file that will configure the VM on first boot. This file contains all user setup, package installation, and initial configuration.

    Store the file at /var/lib/vz/snippets/dev-sandbox-userdata.yaml on the Proxmox host.

    #cloud-config
    hostname: dev-sandbox
    manage_etc_hosts: true
    users:
      - name: sawyer
        groups: sudo, kvm, libvirt
        sudo: ALL=(ALL) NOPASSWD:ALL
        shell: /bin/bash
        lock_passwd: false
        passwd: "$5$YOURSALT$YOURHASH"  # Generate with: openssl passwd -5 'yourpassword'
        ssh_authorized_keys:
          - ssh-ed25519 AAAA... your-key-comment  # Replace with your public key from ~/.ssh/id_ed25519.pub
    chpasswd:
      expire: false
    ssh_pwauth: true
    package_update: true
    package_upgrade: true
    packages:
      - xfce4
      - xfce4-goodies
      - xrdp
      - xorg
      - dbus-x11
      - openjdk-17-jdk
      - git
      - curl
      - wget
      - unzip
      - zip
      - adb
      - build-essential
      - qemu-kvm
      - libvirt-daemon-system
      - bridge-utils
      - cpu-checker
      - htop
      - neofetch
      - firefox
      - qemu-guest-agent
    runcmd:
      - systemctl enable qemu-guest-agent
      - systemctl start qemu-guest-agent
      - echo xfce4-session > /home/sawyer/.xsession
      - chown sawyer:sawyer /home/sawyer/.xsession
      - systemctl enable xrdp
      - systemctl start xrdp
      - echo 'options kvm-intel nested=1' > /etc/modprobe.d/kvm-nested.conf
      - echo 'options kvm-amd nested=1' >> /etc/modprobe.d/kvm-nested.conf
    final_message: "dev-sandbox is ready. Connect via xrdp on port 3389."
    ⚠ Heads up: When using `--cicustom user=` in Proxmox, it completely replaces the auto-generated cloud-init user config. Do NOT use `--ciuser`, `--cipassword`, or `--sshkeys` alongside it — they are silently ignored. All user setup (name, password, sudo, SSH keys, shell) must live inside the custom YAML.
  4. Step 4

    Create and Configure the VM

    Run these commands on the Proxmox host as root to create the VM, import the cloud image disk, attach it, and configure cloud-init.

    # Create VM
    qm create 102 \
      --name dev-sandbox \
      --memory 8192 \
      --cores 4 \
      --net0 virtio,bridge=vmbr0,tag=2 \
      --ostype l26 \
      --machine q35 \
      --cpu host \
      --agent enabled=1
    
    # Import cloud image as disk
    qm importdisk 102 \
      /var/lib/vz/template/iso/ubuntu-24.04-server-cloudimg-amd64.img \
      local-lvm
    
    # Attach disk and set boot order
    qm set 102 \
      --scsihw virtio-scsi-pci \
      --scsi0 local-lvm:vm-102-disk-0 \
      --ide2 local-lvm:cloudinit \
      --boot order=scsi0 \
      --serial0 socket \
      --vga serial0
    
    # Resize disk (30GB is sufficient; expand if needed)
    qm resize 102 scsi0 +28G
    
    # Attach the custom cloud-init user config
    # NOTE: local:snippets maps to /var/lib/vz/snippets/
    qm set 102 --cicustom "user=local:snippets/dev-sandbox-userdata.yaml"
    
    # Start the VM
    qm start 102
  5. Step 5

    Post-Boot: Fix lightdm Configuration

    The cloud image ships with lightdm but no correct session/greeter defaults. Without this fix, xrdp logins fail with "Failed to start session".

    SSH into the VM and create /etc/lightdm/lightdm.conf with the correct session and greeter settings.

    # SSH into the VM
    ssh sawyer@192.168.1.xxx
    
    # Create lightdm configuration
    sudo tee /etc/lightdm/lightdm.conf > /dev/null << 'EOF'
    [Seat:*]
    user-session=xfce
    greeter-session=unity-greeter
    EOF
    
    # Restart lightdm
    sudo systemctl restart lightdm
    ⚠ Heads up: The installed greeter is `unity-greeter`, not `lightdm-gtk-greeter`. Using the wrong greeter name causes the same "Failed to start session" error.
  6. Step 6

    Remote Desktop Access

    Connect to the VM using any RDP client (Microsoft Remote Desktop, Remmina, etc.).

    Connection Details:

    • Host: 192.168.1.xxx
    • Port: 3389
    • Username: sawyer
    • Password: (configured via the passwd hash in cloud-init)

    You should see the XFCE4 desktop environment after logging in.

  7. Step 7

    SSH Access

    SSH access is available from the Proxmox host or any machine with the SSH key configured in cloud-init.

    ssh sawyer@192.168.1.xxx
    ⚠ Heads up: Password authentication is enabled (`ssh_pwauth: true`) as a fallback, but key-based authentication is recommended for security.
  8. Step 8

    Android Development Setup

    After the VM is running, set up the Android SDK manually. The SDK is not included in cloud-init because it requires interactive license acceptance and the download size would slow first boot.

    Download and install command-line tools:

    # Download command-line tools
    mkdir -p ~/android-sdk/cmdline-tools
    cd /tmp
    wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
    unzip commandlinetools-linux-11076708_latest.zip -d ~/android-sdk/cmdline-tools/
    mv ~/android-sdk/cmdline-tools/cmdline-tools ~/android-sdk/cmdline-tools/latest
    
    # Accept licenses and install build tools
    export ANDROID_HOME=~/android-sdk
    export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools
    yes | sdkmanager --licenses
    sdkmanager "platforms;android-34" "build-tools;34.0.0" "platform-tools"
  9. Step 9

    Configure Android SDK Environment Variables

    Add the Android SDK paths to your shell profile so they persist across sessions.

    # Add to ~/.bashrc
    cat >> ~/.bashrc << 'EOF'
    export ANDROID_HOME=~/android-sdk
    export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools
    EOF
    
    # Reload the configuration
    source ~/.bashrc
  10. Step 10

    Configure Git Authentication

    Set up Git with your user information and configure credential storage. This example uses a GitHub Personal Access Token (PAT) for authentication.

    git config --global user.name "Your Name"
    git config --global user.email "your@email.com"
    git config --global credential.helper store
    
    # On next git push/pull, enter PAT as password — it will be cached
  11. Step 11

    Verify Nested Virtualization

    Nested virtualization was enabled via cloud-init in the kernel module configuration. Verify it's working:

    # Check if nested virtualization is enabled
    cat /sys/module/kvm_intel/parameters/nested
    # Expected output: Y (for Intel) or N if not enabled
    
    # Alternative check using kvm-ok
    kvm-ok
    # Expected output: KVM acceleration can be used
  12. Step 12

    Common Pitfalls and Solutions

    Using --ciuser/--cipassword/--sshkeys with --cicustom user=

    • Problem: User never created, SSH auth fails
    • Solution: Put everything in the YAML; drop those flags

    Wrong greeter in lightdm.conf

    • Problem: "Failed to start session" on RDP
    • Solution: Use unity-greeter not lightdm-gtk-greeter

    Not including chpasswd: {expire: false}

    • Problem: Password expired on first login
    • Solution: Already included in the YAML above

    Forgetting --cicustom uses local:snippets/ not a filesystem path

    • Problem: qm set error
    • Solution: Use local:snippets/filename.yaml format

Feature requests

Sign in to suggest features or vote on existing ones.

No feature requests yet.

Discussion

0 people marked this as worked·Sign in to mark your own.

Sign in to join the discussion.

No comments yet.