Creating a Small Disk VM in Azure

Azure charges for OS Disk space, so even if a VM is not running, it costs money to exist. The default Windows 2019 image creates a 128GB disk, and even the Ubuntu images is based on a 30GB disk, and will cost $2.40 or $5.28 monthly depending on Premium_LRS or StandardSSD_LRS. The default in Azure Portal & for ARM templates is to assign Premium SSDs, so mostly my servers have been ~$5/mo just for disk.

cost over time

Recently, I have started running a larger number of VMs with lower compute cost and many of them deallocated most of the time. This has resulted in storage actually becoming the largest component of my bill.

By reducing the Ubuntu base image to 16GB, we can move from a 32GB Premium SSD to a 16GB Standard SSD, saving $4.08 per month. It doesn’t seem like a lot, with 5+ VMs it will add up to hundreds of dollars over a year or more.

Managed Disk Cost / month (as of Nov 2021)

Tier 8GB 16GB 32GB 128GB
Premium_LRS (SSD) $1.48 $2.96 $5.28 $19.71
StandardSSD_LRS (SSD) $0.60 $1.20 $2.40 $9.60
Standard_LRS (HDD) n/a n/a $1.54 $5.89

Azure Managed Disks pricing

VirtualBox & azure won’t allow us to shrink VHD files, so this is my workaround.

Downsides: - This is way more work to save like $1.50/month/VM, vs just switching to StandardSSD_LRS - Image must be manually replicated using Image Gallery, the replica storage might eat up any cost savings unless its deployed a lot.

Down-sizing the Ubuntu VHD File

Requires: VirtualBox, Ubuntu Live CD

TODO: figure out how to do this natively in windows, maybe Hyper-V can do it (requires Windows Pro).

  1. Download & extract a Ubuntu Azure image

  2. Open VirtualBox, create a VM using an ubuntu live cd

  3. Create an empty 16GB VHD file

    • In VirtualBox, File -> Virtual Media Manager
    • Create, VHD, Fixed Size, 16GB, ubuntu-16gb.vhd
  4. In Virtual Media Manager, add the ubuntu-azure image. Set Properties -> Type -> Immutable

  5. On the VM, Settings -> Storage

    • Add a SATA controller for the 30GB ubuntu-azure image

    • Add a SATA controller for the 16GB new image

      VM Storage Settings
  6. Start the VM, boot into the live CD

  1. Copy the contents of the ubuntu-azure image to the empty VHD

The new 16GB ubuntu image looks like this in gparted:

/dev/sdb with partitions

Create an Image in Azure

Requires: azure-cli, AzCopy

# Get the exact size in bytes of the VHD
$ wc -c ubuntu-16.vhd

# Create an empty disk in azure
$ az disk create -n $DISK_NAME -g $RESOURCE_GROUP -l centralus --os-type Linux --for-upload --upload-size-bytes $SIZE_BYTES --sku standard_lrs

# Get access/link to upload VHD blob. It will output the `accessSas` in the JSON output
$ az disk grant-access -n $DISK_NAME -g $RESOURCE_GROUP --access-level Write --duration-in-seconds 86400

# Use AzCopy to upload the VHD
$ azcopy.exe copy ubuntu-16gb.vhd "$BLOB_URL" --blob-type PageBlob

# revoke write access to the disk
$ az disk revoke-access -n $DISK_NAME -g $RESOURCE_GROUP

# Create an image with the disk
$ az image create -n $IMAGE_NAME -g $RESOURCE_GROUP --source $DISK_NAME --os-type Linux

This image can now be used in an Image Gallery and replicated to other regions.

Using the Image

Use the image directly in an ARM template

# Get the image id 
$ az image show -g $RESOURCE_GROUP --name $IMAGE_NAME | jq -r ".id"

In the ARM JSON, set the imageReference to

"imageReference": {
  "id": "$IMAGE_ID"
}

Through Azure Portal, create an “Azure compute gallery”.

Then Add-> VM Image Definition - Region: (any region) - OS type: Linux - OS state: Generalized - VM Generation: Gen 1 - Under ‘Version’, select the image as the source image

Get the resource id from VM image definition -> Properties -> Resource ID

You can also get it from azure-cli:

$ az sig image-definition show -g $RESOURCE_GROUP --gallery-name $GALLERY_NAME --gallery-image-definition $IMAGE_DEFINITION_NAME | jq -r ".id"

Set the imageReference to

"imageReference": {
  "id": "$RESOURCE_ID"
}