Goal
An application needs its own volume for data, logs or database files, separate from the OS disk. You want to add a managed data disk, make it usable in the guest OS, and make sure it's still mounted after the next reboot or redeploy.
Keeping data off the OS disk makes it easier to resize, snapshot or move the data without touching the operating system.
Before you start
Check how many data disks the VM size supports and what's already attached. Each VM size has a maximum data disk count, so check the size documentation for current limits.
RG=rg-prod; VM=vm-app-01
az vm show -g $RG -n $VM --query "{size:hardwareProfile.vmSize, zone:zones, disks:storageProfile.dataDisks[].{lun:lun, name:name, sizeGb:diskSizeGb}}" -o json
SIZE=$(az vm show -g $RG -n $VM --query hardwareProfile.vmSize -o tsv)
LOC=$(az vm show -g $RG -n $VM --query location -o tsv)
az vm list-sizes -l $LOC --query "[?name=='$SIZE'].{size:name, maxDataDisks:maxDataDiskCount}" -o table
If the VM is in an availability zone, the disk must be created in the same zone.
Create and attach the disk
The quickest way is to create and attach in one step. --new creates an empty managed disk:
az vm disk attach -g $RG --vm-name $VM \
--name ${VM}-data-01 --new --size-gb 256 --sku Premium_LRS --lun 0
Or create the disk first when you need more control, for example a zone or a specific tier:
az disk create -g $RG -n ${VM}-data-01 --size-gb 256 --sku Premium_LRS --zone 1
az vm disk attach -g $RG --vm-name $VM --name ${VM}-data-01 --lun 0 --caching ReadOnly
Pick the LUN deliberately. It's how you find the disk inside the guest, and it should not clash with an existing data disk.
| Workload | Host caching |
|---|---|
| Database data files, read-heavy | ReadOnly |
| Database or application log files, write-heavy | None |
| General application data | ReadOnly or None |
Attaching a data disk doesn't need a reboot on current VM sizes.
Initialize the disk on Windows
The new disk appears offline and RAW. Find it by LUN rather than by disk number, because disk numbers can change:
Get-Disk | Where-Object PartitionStyle -eq 'RAW' |
Select-Object Number, FriendlyName, Size, Location
# Initialize, partition and format the RAW disk (use the number from above)
$n = 2
Set-Disk -Number $n -IsOffline $false # needed if the SAN policy left the disk offline
Initialize-Disk -Number $n -PartitionStyle GPT
New-Partition -DiskNumber $n -UseMaximumSize -DriveLetter F |
Format-Volume -FileSystem NTFS -NewFileSystemLabel "Data" -AllocationUnitSize 65536 -Confirm:$false
Get-Volume -DriveLetter F
The Location column includes the LUN, which you can match to the --lun you used. A 64 KB allocation unit is the usual recommendation for SQL Server data and log volumes; the default is fine for general file storage.
Caution
Initialize-DiskandFormat-Volumedestroy whatever is on the target disk. Filter onPartitionStyle -eq 'RAW'and check the size and LUN before running them, especially on servers with several data disks.
Windows remembers the drive letter, so no further step is needed to keep it after a reboot.
Initialize the disk on Linux
Azure Linux images create stable symlinks per LUN under /dev/disk/azure/scsi1/ (the exact path can vary by image and storage controller, so ls it first). Use those links instead of /dev/sdc, because sdX names can change between boots.
ls -l /dev/disk/azure/scsi1/ # lun0 -> ../../../sdc, for example
lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE
DISK=/dev/disk/azure/scsi1/lun0
sudo parted $DISK --script mklabel gpt mkpart primary 0% 100%
sudo partprobe $DISK
sudo mkfs.xfs ${DISK}-part1 # or: sudo mkfs.ext4 ${DISK}-part1
sudo mkdir -p /data
sudo mount ${DISK}-part1 /data
df -h /data
If the -part1 link doesn't exist on your image, use the partition name that lsblk shows, for example /dev/sdc1, only for the format step.
Make the mount persistent with fstab
Mount by UUID, and add nofail so the VM still boots if the disk is ever detached:
UUID=$(sudo blkid -s UUID -o value ${DISK}-part1)
echo "UUID=$UUID /data xfs defaults,nofail 0 2" | sudo tee -a /etc/fstab
# Test the fstab entry before you reboot
sudo umount /data
sudo mount -a
findmnt /data
Use ext4 instead of xfs in the fstab line if you formatted with ext4.
Warning
A wrong fstab line without
nofailcan drop a Linux VM into emergency mode on the next boot, and then you need the Serial Console to fix it. Always runsudo mount -aand check for errors before rebooting.
Verify
az vm show -g $RG -n $VM --query "storageProfile.dataDisks[].{lun:lun, name:name, caching:caching, sizeGb:diskSizeGb}" -o table
- Inside the guest,
Get-Volumeordf -hshows the new volume with the expected size. - Write a test file, reboot the VM, and confirm the volume and file are still there.
- Point the application at the new path and restart it.
Detach a disk safely
When a disk is no longer needed, stop the application, unmount it in the guest (remove the fstab line on Linux, or take the disk offline on Windows), then detach:
az vm disk detach -g $RG --vm-name $VM --name ${VM}-data-01
Detaching does not delete the disk. It keeps costing money until you run az disk delete, so snapshot it first if there's any doubt about the data.
Prevent it next time
- Build it into the template. Add data disks in Bicep or Terraform, and format them with cloud-init or a Custom Script Extension, so every rebuild comes out the same.
- Alert on free space for each data volume, not only the OS disk.
- Include data disks in backups. Azure Backup protects all disks by default, but check that nobody excluded the new one.
- Name disks after the VM and purpose (
vm-app-01-data-01) and tag them, so unattached disks are easy to spot and clean up.