Proposed Pull Request Change

title description author ms.service ms.custom ms.collection ms.topic ms.date ms.author ms.reviewer
Create and upload an Ubuntu Linux VHD in Azure Learn to create and upload an Azure virtual hard disk (VHD) that contains an Ubuntu Linux operating system. vamckMS azure-virtual-machines linux-related-content linux how-to 09/09/2026 vakavuru mattmcinnes, divargas
📄 Document Links
GitHub View on GitHub Microsoft Learn View on Microsoft Learn
⚠ Content Truncation Detected
The generated rewrite appears to be incomplete.
Original lines: -
Output lines: -
Ratio: -
Raw New Markdown
Generating updated version of doc...
Rendered New Markdown
Generating updated version of doc...
+0 -0
+0 -0
--- title: Create and upload an Ubuntu Linux VHD in Azure description: Learn to create and upload an Azure virtual hard disk (VHD) that contains an Ubuntu Linux operating system. author: vamckMS ms.service: azure-virtual-machines ms.custom: linux-related-content ms.collection: linux ms.topic: how-to ms.date: 09/09/2026 ms.author: vakavuru ms.reviewer: mattmcinnes, divargas # Customer intent: "As a cloud administrator, I want to create and upload a custom Ubuntu Linux VHD to Azure so that I can deploy specialized virtual machines tailored to my organization's specific needs." --- # Prepare an Ubuntu virtual machine for Azure **Applies to:** :heavy_check_mark: Linux VMs :heavy_check_mark: Flexible scale sets Ubuntu provides official Azure-ready cloud images (cloudimg) that include all components required for successful provisioning on Azure, such as *cloud-init*, Azure-optimized kernels, Azure guest agent compatibility, and performance-tuned defaults for virtualized environments. These images are the recommended starting point when creating a custom VHD because they align with Azure VM provisioning requirements and reduce the manual configuration steps required. Ubuntu publishes these images in multiple formats, including ready-to-use Azure virtual hard disks (VHDs), on the https://cloud-images.ubuntu.com/ website. If you need to build a specialized Ubuntu image for Azure, you can start with these validated VHD builds and customize them as needed. The latest image releases are available at the following locations: * Ubuntu 20.04/Focal: [focal-server-cloudimg-amd64-azure.vhd.tar.gz](https://cloud-images.ubuntu.com/focal/current/focal-server-cloudimg-amd64-azure.vhd.tar.gz) * Ubuntu 22.04/Jammy: [jammy-server-cloudimg-amd64-azure.vhd.tar.gz](https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64-azure.vhd.tar.gz) * Ubuntu 24.04/Noble: [noble-server-cloudimg-amd64-azure.vhd.tar.gz](https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64-azure.vhd.tar.gz) ## Prerequisites This article assumes that you've already installed an Ubuntu Linux operating system (OS) to a VHD. Multiple tools exist to create .vhd files. An example is a virtualization solution such as Hyper-V. For instructions, see [Install the Hyper-V role and configure a virtual machine (VM)](/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/hh846766(v=ws.11)). ### Ubuntu installation notes > [!IMPORTANT] > The remote NVMe preparation step in this article is optional. Complete it only if you're creating an image that you intend to deploy with a remote NVMe disk controller. If the target VM uses SCSI, skip the remote NVMe step and retain the standard settings. Remote NVMe requires a Gen2 image. Verify that the Ubuntu release is listed in [Supported OS images for remote NVMe](../enable-nvme-interface.md) and that the exact target VM size advertises NVMe in its `DiskControllerTypes` capability. * For more tips on preparing Linux for Azure, see [General Linux installation notes](create-upload-generic.md#general-linux-installation-notes). * The VHDX format isn't supported in Azure, only *fixed VHD*. You can convert the disk to VHD format by using Hyper-V Manager or the `Convert-VHD` cmdlet. * When you install the Linux system, we recommend that you use standard partitions rather than Logical Volume Manager (LVM), which is often the default for many installations. These standard partitions avoid LVM name conflicts with cloned VMs, particularly if an OS disk ever needs to be attached to another VM for troubleshooting. [LVM](/previous-versions/azure/virtual-machines/linux/configure-lvm) or [RAID](/previous-versions/azure/virtual-machines/linux/configure-raid) can also be used on data disks. * Don't configure a swap partition or swap file on the OS disk. You can configure the `cloud-init` provisioning agent to create a swap file or a swap partition on the temporary resource disk. For more information about this process, see [Create a SWAP partition for an Azure Linux VM](/troubleshoot/azure/virtual-machines/linux/create-swap-file-linux-vm). * All VHDs on Azure must have a virtual size aligned to 1 MB. When you convert from a raw disk to VHD, you must ensure that the raw disk size is a multiple of 1 MB before conversion. For more information, see [Linux installation notes](create-upload-generic.md#general-linux-installation-notes). > [!IMPORTANT] > Swap guidance that uses the temporary resource disk applies only to VM sizes that include local temporary storage. The remote disk controller type, SCSI or NVMe, doesn't determine whether a resource disk is available. You can upload a prebuilt Ubuntu image directly to Azure and use the resulting VHD to create new virtual machines. If this is your first time uploading a `.vhd` file, see [Create a Linux VM from a custom disk](upload-vhd.md#option-1-upload-a-vhd). ## Manual steps > [!NOTE] > Before you attempt to create your own custom Ubuntu image for Azure, consider using the prebuilt and tested images from the [Ubuntu Cloud Images webpage](https://cloud-images.ubuntu.com/) instead. > If you are using Windows to Extract the prebuilt it image , please use Windows WSL and run the following command ```bash sudo tar --sparse -xvzf <image name>-azure.vhd.tar.gz ``` 1. In the center pane of Hyper-V Manager, select the VM. 1. Select **Connect** to open the window for the VM. 1. Replace the current repositories in the image to use Ubuntu's Azure repository. Before you edit `/etc/apt/sources.list` `/etc/apt/sources.list.d/ubuntu.sources`, we recommend that you make a backup: For Ubuntu 22.04 and earlier ```bash sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak ``` For Ubuntu 24.04 and later ```bash sudo cp /etc/apt/sources.list.d/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources.bak ``` For Ubuntu 22.04 and earlier ```bash sudo sed -i 's#http://archive\.ubuntu\.com/ubuntu#http://azure\.archive\.ubuntu\.com/ubuntu#g' /etc/apt/sources.list sudo sed -i 's#http://[a-z][a-z]\.archive\.ubuntu\.com/ubuntu#http://azure\.archive\.ubuntu\.com/ubuntu#g' /etc/apt/sources.list sudo sed -i 's#http://security\.ubuntu\.com/ubuntu#http://azure\.archive\.ubuntu\.com/ubuntu#g' /etc/apt/sources.list sudo sed -i 's#http://[a-z][a-z]\.security\.ubuntu\.com/ubuntu#http://azure\.archive\.ubuntu\.com/ubuntu#g' /etc/apt/sources.list sudo apt update ``` For Ubuntu 24.04 and later ```bash sudo sed -i 's#http://archive\.ubuntu\.com/ubuntu#http://azure\.archive\.ubuntu\.com/ubuntu#g' /etc/apt/sources.list.d/ubuntu.sources sudo sed -i 's#http://[a-z][a-z]\.archive\.ubuntu\.com/ubuntu#http://azure\.archive\.ubuntu\.com/ubuntu#g' /etc/apt/sources.list.d/ubuntu.sources sudo sed -i 's#http://security\.ubuntu\.com/ubuntu#http://azure\.archive\.ubuntu\.com/ubuntu#g' /etc/apt/sources.list.d/ubuntu.sources sudo sed -i 's#http://[a-z][a-z]\.security\.ubuntu\.com/ubuntu#http://azure\.archive\.ubuntu\.com/ubuntu#g' /etc/apt/sources.list.d/ubuntu.sources sudo apt update ``` 1. The Ubuntu Azure images use Azure-Tailored Kernels [Azure-tailored kernel](https://ubuntu.com/blog/microsoft-and-canonical-increase-velocity-with-azure-tailored-kernel). Update the OS to the latest Azure-tailored kernel and install Azure Linux tools (including Hyper-V dependencies): ```bash sudo apt update sudo apt install linux-azure linux-image-azure linux-headers-azure linux-tools-common linux-cloud-tools-common linux-tools-azure linux-cloud-tools-azure sudo apt full-upgrade sudo reboot ``` 1. Modify the kernel boot line for GRUB to include extra kernel parameters for Azure. To do this step, open `/etc/default/grub` in a text editor, find the variable called `GRUB_CMDLINE_LINUX_DEFAULT` (or add it if needed), and edit it to include the following parameters: ```config GRUB_CMDLINE_LINUX_DEFAULT="console=tty1 console=ttyS0,115200n8 earlyprintk=ttyS0,115200 rootdelay=300 quiet splash" ``` 1. Save and close this file, and then run `sudo update-grub`. This step ensures that all console messages are sent to the first serial port, which can assist Azure technical support with debugging issues. 1. **Only if you're creating an image for a remote NVMe disk controller**, configure and verify NVMe boot support. Otherwise, skip this step. 1. Make the NVMe drivers persistent so they're added to every future initramfs, and then rebuild the current initramfs. The loop skips modules that are already listed, which avoids duplicate entries: ```bash for module in nvme nvme_core; do grep -qxF "$module" /etc/initramfs-tools/modules || echo "$module" | sudo tee -a /etc/initramfs-tools/modules done sudo update-initramfs -u -k all ``` 1. Add the Azure NVMe I/O timeout to the kernel command line, and then rebuild the GRUB configuration so the setting persists across reboots: ```bash sudo sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT=/ s/"$/ nvme_core.io_timeout=240"/' /etc/default/grub sudo update-grub ``` 1. Confirm that both NVMe modules exist in the kernel's module tree so they can be packaged into initramfs. This check reads the on-disk module files, so it succeeds even on a SCSI preparation VM and doesn't imply that NVMe is loaded or in use: ```bash modinfo nvme modinfo nvme_core ``` Each command must return module details. If either module isn't found, install or enable it by following the Ubuntu kernel documentation before you continue. 1. Confirm that the rebuilt initramfs package includes both NVMe drivers. This check confirms the image is ready: ```bash sudo lsinitramfs /boot/initrd.img-$(uname -r) | grep -E 'nvme(_core)?\.ko' ``` The output must list both the `nvme` and `nvme_core` drivers. After the image boots on an NVMe VM, verify the runtime timeout by running `cat /sys/module/nvme_core/parameters/io_timeout`; the expected value is `240`. 1. For both SCSI and NVMe images, use file-system UUIDs or another persistent identifier in `/etc/fstab`. Don't use `/dev/sd*` or `/dev/nvme*` device names, because device names can change across reboots or when the disk controller changes. 1. List the block devices and their persistent identifiers so that you can compare them with the entries in `/etc/fstab`: ```bash sudo blkid ``` 1. Check `/etc/fstab` for syntax errors, invalid mount options, and references that can't be resolved: ```bash sudo findmnt --verify --verbose ``` 1. Ensure that the SSH server is installed and configured to start at boot time. This setting is usually the default. 1. Install `cloud-init` (the provisioning agent) and the Azure Linux agent (the guest extensions handler). `Cloud-init` uses `netplan` to configure the system network configuration (during provisioning and each subsequent boot) and `gdisk` to partition resource disks. ```bash sudo apt update sudo apt install cloud-init gdisk netplan.io walinuxagent && systemctl stop walinuxagent ``` > [!NOTE] > The `walinuxagent` package might remove the `NetworkManager` and `NetworkManager-gnome` packages, if they're installed. 1. Remove `cloud-init` default configurations and leftover `netplan` artifacts that might conflict with `cloud-init` provisioning on Azure: For Ubuntu 22.04 and earlier ```bash sudo rm -f /etc/cloud/cloud.cfg.d/50-curtin-networking.cfg /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg /etc/cloud/cloud.cfg.d/99-installer.cfg /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg sudo rm -f /etc/cloud/ds-identify.cfg sudo rm -f /etc/netplan/*.yaml ``` For Ubuntu 24.04 and later ```bash sudo rm -f /etc/cloud/cloud.cfg.d/curtin-preserve-sources.cfg /etc/cloud/cloud.cfg.d/99-installer.cfg /etc/cloud/cloud.cfg.d/subiquity-disable-cloudinit-networking.cfg sudo rm -f /etc/cloud/ds-identify.cfg sudo rm -f /etc/netplan/*.yaml ``` 1. Configure `cloud-init` to provision the system by using the Azure data source: ```bash sudo tee /etc/cloud/cloud.cfg.d/90_dpkg.cfg <<EOF datasource_list: [ Azure ] EOF sudo tee /etc/cloud/cloud.cfg.d/90-azure.cfg <<EOF system_info: package_mirrors: - arches: [i386, amd64] failsafe: primary: http://archive.ubuntu.com/ubuntu security: http://security.ubuntu.com/ubuntu search: primary: - http://azure.archive.ubuntu.com/ubuntu/ security: [] - arches: [armhf, armel, default] failsafe: primary: http://ports.ubuntu.com/ubuntu-ports security: http://ports.ubuntu.com/ubuntu-ports EOF sudo tee /etc/cloud/cloud.cfg.d/10-azure-kvp.cfg <<EOF reporting: logging: type: log telemetry: type: hyperv EOF ``` 1. Configure the Azure Linux agent to rely on `cloud-init` to perform provisioning. For more information on these options, look at the [WALinuxAgent project](https://github.com/Azure/WALinuxAgent). ```bash sudo sed -i 's/Provisioning.Enabled=y/Provisioning.Enabled=n/g' /etc/waagent.conf sudo sed -i 's/Provisioning.UseCloudInit=n/Provisioning.UseCloudInit=y/g' /etc/waagent.conf sudo sed -i 's/ResourceDisk.Format=y/ResourceDisk.Format=n/g' /etc/waagent.conf sudo sed -i 's/ResourceDisk.EnableSwap=y/ResourceDisk.EnableSwap=n/g' /etc/waagent.conf ``` ```bash sudo tee -a /etc/waagent.conf <<EOF # For Azure Linux agent version >= 2.2.45, this is the option to configure, # enable, or disable the provisioning behavior of the Linux agent. # Accepted values are auto (default), waagent, cloud-init, or disabled. # A value of auto means that the agent will rely on cloud-init to handle # provisioning if it is installed and enabled, which in this case it will. Provisioning.Agent=auto EOF ``` 1. Clean `cloud-init` and Azure Linux agent runtime artifacts and logs: ```bash sudo cloud-init clean --logs --seed sudo rm -rf /var/lib/cloud/ sudo systemctl stop walinuxagent.service sudo rm -rf /var/lib/waagent/ sudo rm -f /var/log/waagent.log ``` 1. Deprovision the VM and prepare it for provisioning on Azure. > [!NOTE] > The `sudo waagent -force -deprovision+user` command generalizes the image by attempting to clean the system and make it suitable for reprovisioning. The `+user` option deletes the last provisioned user account and associated data. ```bash sudo waagent -force -deprovision+user sudo rm -f ~/.bash_history ``` > [!WARNING] > Deprovisioning by using the preceding command doesn't guarantee that the image is cleared of all sensitive information and is suitable for redistribution. 1. Select **Action** > **Shut Down** in Hyper-V Manager. 1. Azure only accepts fixed-size VHDs. If the VM's OS disk isn't a fixed-size VHD, use the `Convert-VHD` PowerShell cmdlet and specify the `-VHDType Fixed` option. For more information, look at the docs for `Convert-VHD` at [Convert-VHD](/powershell/module/hyper-v/convert-vhd). 1. To bring a Generation 2 VM on Azure, create the fallback UEFI boot path: 1. Copy the `ubuntu` EFI directory to a new directory named `boot`: ```bash sudo cp -rp /boot/efi/EFI/ubuntu /boot/efi/EFI/boot ``` 1. Rename the EFI loader to the fallback boot loader name: ```bash sudo mv /boot/efi/EFI/boot/shimx64.efi /boot/efi/EFI/boot/bootx64.efi ``` 1. Rename the GRUB configuration file to the fallback name: ```bash sudo mv /boot/efi/EFI/boot/grub.cfg /boot/efi/EFI/boot/bootx64.cfg ``` ## Related content You're now ready to use your Ubuntu Linux VHD to create new VMs in Azure. If this is the first time that you're uploading the .vhd file to Azure, see [Create a Linux VM from a custom disk](upload-vhd.md#option-1-upload-a-vhd). For an existing Azure VM that you need to move from SCSI to NVMe, use [Convert Linux and Windows VMs from SCSI to NVMe](../nvme-linux.md). For architecture and support information, see [NVMe overview](../nvme-overview.md).
Success! Branch created successfully. Create Pull Request on GitHub
Error: