In the previous post we saw how to configure networking on first boot with cloud-init. If your Pi has a reliable network connection during that first boot (preferably Ethernet, given the current wifi issue described in LP: #1870346) you can also opt to have cloud-init install whatever packages you’re going to need (and configure them too!) so your system is completely ready for you when you login.
First, it’s useful to learn how to get the local package index up to date (especially if you’re dealing with an old image). This can be done by adding the following line to the user-data file (that one again!) on the boot partition:
If you also want to upgrade the system to the latest version of all installed packages on the initial boot, you can add:
As you can tell, on Ubuntu these effectively run the “apt update” and “apt upgrade” commands, but cloud-init also supports several other distros, hence the rather generic naming of these options. How about installing extra packages? This is also simple to arrange:
# Install some things that Dave probably ought to consider including on # the images by default... packages: - pi-bluetooth - iw
You can specify a particular version of a package by using a list, but this generally isn’t a good idea (there’s no guarantee that version is going to be available unless you’re installing from, say, a repo you control like a PPA):
packages: - [nginx-light, 1.17.9-0ubuntu3]
Speaking of which: what if you want to include a PPA as a source?
apt: sources: my-ppa: source: "ppa:waveform/pibootctl" # quote the source
This may seem like an overly convoluted series of structures (“source” within a label within “sources” within “apt”) but the reason for this is that there are a great many options for apt, and a number of different means for specifying third party repos (including importing of signing keys, and templates for deriving new repos from existing ones).
A couple more features are worth introducing in the context of installing packages. Firstly, the ability to write arbitrary files. This is particularly useful for providing configuration for packages you’ve installed:
write_files: - path: /etc/default/keyboard content: | # KEYBOARD CONFIGURATION FILE XKBMODEL="pc105" XKBLAYOUT="gb" XKBVARIANT="" XKBOPTIONS="ctrl: nocaps" BACKSPACE="guess"
And finally, the ultimate blunt tool of “runcmd” which effectively lets you run arbitrary commands as root on the first boot; use with caution! In this case we can use it to set the wireless regulatory domain:
runcmd: - iw reg set GB
Putting it all together
Here’s a user-data file I use when testing Pi daily images. It sets up the Pi so I can access it over SSH, importing my keys from GitHub, installs some extra packages for WiFi and Bluetooth, configures the WiFi regulatory domain, adds the PPA for pibootctl (a little project I’ve been working on for boot configuration manipulation):
#cloud-config chpasswd: expire: false list: - ubuntu:ubuntu hostname: pitest apt: sources: my-ppa: source: "ppa:waveform/pibootctl" # the quotes are mandatory package_update: true package_upgrade: true packages: - pi-bluetooth - iw - pibootctl runcmd: - iw reg set GB ssh_pwauth: false ssh_import_id: - gh:waveform80 write_files: - path: /etc/default/keyboard content: | # KEYBOARD CONFIGURATION FILE XKBMODEL="pc105" XKBLAYOUT="gb" XKBVARIANT="" XKBOPTIONS="ctrl: nocaps" BACKSPACE="guess" runcmd: - reboot
At the moment this file lives on my desktop and, after flashing a fresh image, the first thing I do is copy that file onto the boot partition, replacing the existing user-data. Then I boot the Pi and leave it for a few minutes to sort everything out!
Hopefully that gives you some ideas for playing with. And remember, you can throw these same configurations at various cloud instances too (which can be quite handy if you ever need to scale things up temporarily!)
Next I’m going to take a short break and see if I can set up some form of comments here (probably via the lazy disqus route), and then I might delve into a series on pibootctl or something.