Skip to content

Instantly share code, notes, and snippets.

Last active Nov 10, 2022
What would you like to do?
cloud-init ubuntu nocloud example with network config

Boot Ubuntu providing it network config in NoCloud Datasource

Ubuntu images can be booted with an additional disk that provides user-data, meta-data and network configuration. This is described some in the upstream documentation.

But this example allows for more easily tinkering.

See a screencast of this on

What is here

A brief description of files here.


Heres an example walking through how to use this.

  • Verify dependencies

    $ ./check-dependencies
    All good. (cloud-localds genisoimage qemu-img qemu-system-x86_64 wget)
  • Download an image

    Original images are compressed qcow2. get-image downloads an original image and converts it to 'raw' format. This is not strictly necessary, but booting a compressed qcow2 directly means that all disk reads go through decompression.

    $ ./get-image xenial
    downloading <...>
    dist: xenial-server-cloudimg-amd64-disk1.img
    raw:  xenial-server-cloudimg-amd64.raw
  • Build a seed image from provided files

    cloud-localds creates an iso9660 filesystem with expected content. Specifically the filesystem label is 'cidata', and the top level directory has 'meta-data', 'user-data', and 'network-config' files. network-config is optional.

    $ cloud-localds -v --network-config=network-config-v1.yaml \
        seed.img user-data.yaml meta-data.yaml
    wrote seed.img with filesystem=iso9660 and diskformat=raw
  • Create a qcow2 disk named 'disk.img' backed by the raw image

    By using a qcow image backed by the raw, we can avoid modifying the original.

    $ qemu-img create -f qcow2 -b xenial-server-cloudimg-amd64.raw disk.img
  • Boot the image

    By default, boot will run qemu in vga graphics mode, and create a window on your system. It will boot to a login prompt and you can login with username ubuntu and password passw0rd.

    I like to boot with qemu's -nographic option, which will display the full boot in a terminal which is better for demos. See a full boot log here

    Some things to look at once you've logged in

    $ cat /run/cloud-init/result.json
     "v1": {
      "datasource": "DataSourceNoCloud [seed=/dev/sr0][dsmode=net]",
      "errors": []
    $ grep -v '^[#]' /etc/network/interfaces.d/50-cloud-init.cfg
    auto lo
    iface lo inet loopback
    auto interface0
    iface interface0 inet static
    $ grep INFO /var/log/cloud-init.log 
    2017-11-03 18:10:18,785 -[INFO]: Loaded datasource DataSourceNoCloud - DataSourceNoCloud [seed=/dev/sr0][dsmode=net]
    2017-11-03 18:10:18,964 -[INFO]: Applying network configuration from ds bringup=False: {'config': [{'type': 'physical', 'mac_address': '52:54:00:12:34:00', 'name': 'interface0', 'subnets': [{'netmask': '', 'gateway': '', 'type': 'static', 'address': ''}]}], 'version': 1}
    2017-11-03 18:10:22,954 -[INFO]: No custom template provided, fall back to builtin
Usage() {
cat <<EOF
Usage: ${0##*/} disk.img seed.img [qemu [additional [args]]]
fail() { echo "$@" 1>&2; exit 1; }
set -f
[ "$1" = "-h" -o "$1" = "--help" ] && { Usage; exit; }
[ $# -ge 2 ] || { Usage 1>&2; exit 1; }
shift 2
[ "$1" = "--" ] && shift
[ -f "$disk" ] || fail "$disk: not a file"
# provide 'format=' correctly so qemu doesnt complain. (qcow2 or raw)
fmt=$(qemu-img info "$disk" | awk -F: '$1 == "file format" { print $2 }')
fmt=${fmt# }
# add -m 768 if no '-m' present.
args=" $* "
[ "${args##* -m}" = "-m" ] || set -- "$@" -m 768
set -- \
qemu-system-x86_64 -enable-kvm \
-netdev type=user,id=net00 \
-device virtio-net-pci,netdev=net00,mac=${MAC} \
-drive file=$disk,id=bootdisk,if=none,format=$fmt,index=0 \
-device virtio-blk,drive=bootdisk \
-drive file=$seed,id=seed,if=none,format=raw,index=1 \
-device virtio-blk,drive=seed \
echo "$@" 1>&2
# vi: ts=4 expandtab
checkdep() {
local exe="$1" package="$2" upstream="$3"
if command -v "$1" >/dev/null 2>&1; then
FOUND="${FOUND:+${FOUND} }$exe"
return "0"
echo "missing $exe."
echo " It can be installed in package: $package"
[ -n "$upstream" ] &&
echo " Upstream project url: $upstream"
return 1
checkdep cloud-localds cloud-image-utils
checkdep genisoimage genisoimage
checkdep qemu-img qemu-utils
checkdep qemu-system-x86_64 qemu-system-x86
checkdep wget wget
if [ -n "$MISSING" ]; then
[ -n "${FOUND}" ] && echo "found: ${FOUND}"
echo "install missing deps with:"
echo " apt-get update && apt-get install ${MISSING}"
echo "All good. (${FOUND})"
# vi: ts=4 expandtab
Usage() {
cat <<EOF
Usage: ${0##*/} release
release is like 'xenial' or 'artful'.
- Downloads an image from
- converts it to raw format (from qcow2)
fail() { echo "$@" 1>&2; exit 1; }
[ "$1" = "-h" -o "$1" = "--help" ] && { Usage; exit 0; }
[ -n "$rel" ] || { Usage 1>&2; fail "Must give release"; }
# trusty and xenial have a '-disk1.img' while
# other releases have just 'disk.img'
case "$rel" in
precise|trusty|xenial) ofname="$rel-server-cloudimg-amd64-disk1.img";;
if [ ! -f "$fname" ]; then
echo "downloading $url to $fname"
wget "$url" -O "$fname.tmp" &&
mv "$fname.tmp" "$fname" || exit
rm -f "$raw"
if [ ! -f "$raw" ]; then
echo "converting $fname to raw in $raw"
qemu-img convert -O raw "$fname" "$raw.tmp" &&
mv "$raw.tmp" "$raw" || exit
rm -f "$pfname"
echo "dist: $ofname"
echo "raw: $raw"
# vi: ts=4 expandtab
instance-id: 506f2788-9741-4ed8-af53-c6a21383d09a
version: 1
- type: physical
name: interface0
mac_address: "52:54:00:12:34:00"
- type: static
version: 2
mac_address: "52:54:00:12:34:00"
set-name: interface0
password: passw0rd
chpasswd: { expire: False }
ssh_pwauth: True
Copy link

jamesbeedy commented Nov 4, 2017

@smoser this is great! thank you!

Copy link

brennancheung commented Oct 13, 2019

Thank you! I wish I found this earlier. It's been nearly 2 days trying to get cloud-init working on my local Ubuntu box with kvm. It finally works. I didn't know I had to put networking in a different section and when building the seed.img.

Copy link

dcdh commented Dec 9, 2020

It helps me a lot. Many thanks !

Copy link

zogness commented Apr 26, 2022

qemu-img create -f qcow2 -b xenial-server-cloudimg-amd64.raw disk.img
I beg your pardon but why would you name the qcow output file "disk.img" when you're explicitly creating a qcow image file?

Copy link

electropolis commented Nov 10, 2022

network config doesn't work
RuntimeError: Unknown network config version: None

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment