Debian, Ubuntu and cloud-init
About Debian and Ubuntu cloud images and cloud-init.
Published on
- Cloud images
- Cloud-init configuration
- Deployment
- Resize the image
- Test the cloud-init configuration
- Create VHDX for Hyper-V
Cloud images
Debian
Download the image that you want form the official Debian images site. Do not forget the file with the hashes. Example:
# download the QCow2 image
wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.qcow2
# or the raw image
wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.tar.xz
# and the file with hashes
wget https://cloud.debian.org/images/cloud/bookworm/latest/SHA512SUMS
# (optional) make the source images read only
chmod 400 debian-12-generic-amd64.qcow2
chmod 400 debian-12-generic-amd64.tar.xz
# check the hash of downloaded image(s):
sha512sum --check --ignore-missing SHA512SUMS
Note: There is no signature file to verify the integrity of SHA512SUMS
:
For the current official images (in the per-distribution directories), the safest method is to download the image and checksum files over TLS from cloud.debian.org or cdimage.debian.org. These names support DNSSEC, so a validating resolver can ensure that a client is connected to a Debian host. And TLS ensures that the data is not manipulated in flight.
Ubuntu
To select, download and verify Ubuntu cloud images see the Ubuntu Cloud Images article.
Cloud-init configuration
You need at least two files, user-data
and meta-data
. meta-data
can be empty and user-data
can be as simple as:
#cloud-config
password: password
chpasswd:
expire: False
which set the password for the default debian
user to password
when used with a Debian cloud image (tested with debian-12-generic-amd64
).
A more useful configuration is in the next script that creates:
- a
user-data
file that:- set the hostname, locale and keyboard layout;
- skip the creation of default user - the existence of the
users:
key does this; - create a user with elevated privileges and add a ssh key for it. Change the
name
andssh-ed25519 AAAA...
as you want; - customize the
/etc/issue
to show the IPv4 address -man getty
for more information.
- an empty
meta-data
if none existed; - an
iso
that can be used to pass configured settings to a cloud image.
#!/bin/bash
cat > user-data << 'EOF'
#cloud-config
hostname: cloud-image
manage_etc_hosts: True
keyboard:
layout: us
locale: en_US.UTF-8
users:
- name: calin
groups: sudo
gecos: System Administrator
lock_passwd: True
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- 'ssh-ed25519 AAAA...'
write_files:
- path: /etc/issue
content: |
\e{lightcyan}\S \e{gray}\n \e{lightgreen}\4 \e{reset}\l
EOF
[[ -f meta-data ]] || touch meta-data
if ! command -v cloud-localds >/dev/null 2>&1; then
sudo apt update && \
sudo apt -y install cloud-image-utils
fi
cloud-localds seed.iso user-data meta-data
For more information about cloud-init
, its modules and examples, see the cloud-init documentation and keep a link to cloud-init Reference close.
Deployment
This previous script creates an iso
file for distribution of cloud-init
configuration but there are other ways. See the:
- NoCloud datasource for local deployment, when you do not use a cloud;
- Datasources document for a specific cloud.
Resize the image
The cloud images are small. Before deployment to production you may want to resize them:
chmod 600 image_file_name.qcow2
qemu-img resize -f qcow2 image_file_name.qcow2 32G
At the first boot, the cloud images (I tested only Debian 12 and Ubuntu 24) will automatically resize the root
partition and the root file system
to fill available space so you don’t have to.
Note: with qemu-img resize
you may skip format specification for qcow2
but is mandatory for raw
images.
Test the cloud-init configuration
I use QEMU and LXD to test cloud-init
configurations before deployment. For LXD
and QEMU
read on but for more information read How to run cloud-init locally
Test with LXC virtual machine
A LXC virtual machine can use a raw
disk. Extract the compressed image, rename the raw disk and make it as read-only:
tar -xf debian-12-generic-amd64.tar.xz
mv disk.raw debian-12-generic-amd64.raw
chmod 400 debian-12-generic-amd64.raw
Assuming user-data
is the cloud-init
configuration file, use this script to create a VM and start it:
# make a copy of the source cloud image
[[ -f d12test.raw ]] && rm -f d12test.raw
cp debian-12-generic-amd64.raw d12test.raw
# the disk image should be writable by the user
chmod 600 d12test.raw
# create an empty VM
lxc init d12test --vm --empty
# add d12test.raw as main disk with high boot priority
lxc config device add d12test disk_media disk source="$(pwd)/d12test.raw" boot.priority=9
# add a second disk for cloud-init configuration
lxc config device add d12test config_media disk source=cloud-init:config boot.priority=0
# load `cloud-init.user-data` from the file `user-data`
lxc config set d12test cloud-init.user-data - < user-data
# start the VM
lxc start d12test --console=vga
You can connect with SSH to the newly created and configured virtual machine.
If /etc/issue
is customized by cloud-init
, like presented in the Cloud-init configuration chapter, you should see the IPv4 address of the newly created virtual machine.
Test with LXC container
lxc launch ubuntu:noble ubuntu-noble-test --config=user.user-data="$(cat user-data)"
Connect to the container with:
lxc exec ubuntu-noble-test /bin/bash
or use lxc list
to see the IP address of ubuntu-noble-test
container and connect to it with SSH.
Test with QEMU
# make a copy of the source cloud image
cp debian-12-generic-amd64.qcow2 d12test.qcow2
# the disk image should be writable by the user
chmod 600 d12test.qcow2
# create a VM and forward guest SSH server to the host's port 2222
qemu-system-x86_64 -m 2048 -net nic -net user,hostfwd=tcp::2222-:22 \
-drive file=d12test.qcow2,index=0,format=qcow2,media=disk \
-drive file=seed.iso,index=1,media=cdrom \
-machine accel=kvm:tcg
Now you should be able to connect with SSH to the new virtual machine. Here is an example SSH config:
Host d12test
HostName localhost
Port 2222
User calin
IdentityFile ___full_path_to_private_key___
IdentitiesOnly yes
Usage on Hyper-V
Create VHDX image
This script:
- downloads a compressed
raw
image and the hashes; - checks the hash of downloaded file;
- resize the
raw
image to32G
; - creates a dynamic VHDX image with the block size set to 1M as in Best Practices for running Linux on Hyper-V.
#!/bin/bash
wget https://cloud.debian.org/images/cloud/bookworm/latest/SHA512SUMS
wget https://cloud.debian.org/images/cloud/bookworm/latest/debian-12-generic-amd64.tar.xz
sha512sum --check --ignore-missing SHA512SUMS
tar -xf debian-12-generic-amd64.tar.xz
qemu-img resize -f raw disk.raw 32G
qemu-img convert -f raw -O vhdx -o subformat=dynamic,block_size=1M disk.raw debian-12-generic-amd64.vhdx
qemu-img info debian-12-generic-amd64.vhdx
rm disk.raw
The virtual machine
In Hyper-V
create a virtual machine with:
- secure boot
Enabled
with theMicrosoft UEFI Certificate Authority
template - a copy of
debian-12-generic-amd64.vhdx
as hard drive seed.iso
as DVD image
When you start the virtual machine with /etc/issue
customized by cloud-init
you should see the IPv4 address of the newly started virtual machine.