This is the list of things I do to create a headless version of the standard Raspbian “wheezy” OS. While the standard OS is a good starting point for exploring the Raspberry Pi world, it isn’t what I want on my remote servers and controllers. For those devices I want a small, secure OS running only the software needed to do the job at hand, whether that’s a simple control program or a full blown web server. This article gives a concise set of command line instructions for creating that OS on a Raspberry Pi (RPi) and then taking a snapshot of it. The snapshot (disk image) can then be burned onto an SD card as the starting point for another headless RPi.
The basic steps for creating the disk image are:
- burn the standard OS onto an SD card and boot the RPi
- configure the RPi’s IP address and SSH into it
- do basic system configuration using raspi-config
- remove the GUI, developer tools, and other software that isn’t required
- update the system software and firmware to the latest versions
- do some cleanup and shutdown the RPi
- create a disk image from the SD card
This results in a disk image that easily fits on a 2GB SD card, leaving plenty of room for apps, control programs, web software, and whatever else. That disk image and the scripts used to produce it are available on the downloads page.
Burn The Standard OS onto an SD Card (Mac)
The first step in creating a headless OS is to get the standard OS onto an SD card and boot the RPi from it. These instructions show how to do this from the command line on a Mac, but there are also several other ways to do it.
- insert a 2GB SD card
- use diskutil list to get the SD card’s device (usually /dev/disk1)
- burn the image onto the SD card
diskutil unmountDisk /dev/disk1 dd if=2012-12-16-wheezy-raspbian.img of=/dev/disk1 bs=1m diskutil eject /dev/disk1
- insert the SD card into the RPi, plug in the power, and give it a minute to boot
Set the RPi’s IP Address (Router)
The RPi comes configured to use DHCP to get an IP address while booting. That’s great, but the IP address can change on later boots, and we need each headless RPi to always have the same IP address so we can find them easily on the network. The simplest way to make this happen is to have the DHCP server (usually your router) assign a fixed IP address to each RPi based on their MAC addresses. So once the RPi has booted, go to your DHCP server and:
- find the RPi in the list of allocated DHCP addresses and record its MAC address
- assign a fixed IP address to the RPi’s MAC address (ex. 192.168.0.101)
- reboot the RPi using SSH with the current DHCP address (ssh pi@address) and the sudo reboot command
SSH and SFTP into the RPi (Mac)
The standard OS includes OpenSSH, which provides secure command line (SSH) and file transfer (SFTP) access to the RPi, and that’s all we need to configure and work with a headless device. The SSH command provides a command line interface using the standard bash shell. Just pass it the user name and IP address and it will prompt for the password:
If you’re switching between multiple OS’s and/or devices, it may sometimes be necessary to clear the keys associated with an SSH device with a command like this:
ssh-keygen -R 192.168.0.101
Run the Pi Configuration Program (RPi)
Once your RPi is up and running, you have a known IP address for it, and you have connected to it with SSH, you’ll need to run the raspi-config program to do some basic system configuration. Notice that we are NOT changing the password or expanding the file system, two steps that are common when setting up a new system. That’s because we’re making a base system that will be used to create other systems. It’s when we create those other systems that we’ll perform those two steps.
For our headless OS, all we’re going to do with raspi-config is:
- sudo raspi-config
- update raspi-config
- set locale to US and UTF-8 (or whatever you need)
- disable en_GB.UTF-8 UTF-8
- enable en_US.UTF-8 UTF-8
- set default to en_US.UTF-8
- set timezone to ‘America/Winnipeg’ (or whatever you need)
- set memory split to 16
- enable ssh
- disable booting to the desktop
- finish and reboot
Remove Unnecessary Things (RPi)
Now that we have a basic system configured, it’s time to start removing things. But first, let’s see how much flash space we’re using and how much is available with the df -h command. The first couple of lines of the output should look something like this:
Filesystem Size Used Avail Use% Mounted on rootfs 1.8G 1.5G 215M 88% /
This shows us that a large percentage of the flash space is already used, leaving very little free space. We’re going to change that by removing the GUI, developer tools, and anything else we can find that isn’t required on a headless device. Each of the following commands (sudo ./remove.sh as a script) will free up a significant amount of space, while the autoremove command at the end will free up all the configuration data associated with the removed programs.
sudo rm -rf /opt ~/python_games sudo apt-get --yes purge `dpkg --get-selections | grep "^lx" | sed s/install//` sudo apt-get --yes purge `dpkg --get-selections | grep xserver | sed s/install//` sudo apt-get --yes purge xarchiver xauth xdg-utils xfonts-encodings xfonts-utils xinit xkb-data xpdf xz-utils sudo apt-get --yes purge `dpkg --get-selections | grep -v deinstall | grep x11 | sed s/install//` sudo apt-get --yes purge x11-xserver-utils libfontenc1 aspell-en libaspell15 desktop-file-utils sudo apt-get --yes purge fontconfig fontconfig-config fonts-droid fonts-freefont-ttf gsfonts sudo apt-get --yes purge gnome-accessibility-themes icelib menu-xdg omxplayer penguinspuzzle plymouth sudo apt-get --yes purge `dpkg --get-selections | grep "\-dev" | sed s/install//` sudo apt-get --yes purge `dpkg --get-selections | grep -v deinstall | grep python | sed s/install//` sudo apt-get --yes purge `dpkg --get-selections | grep -v deinstall | grep sound | sed s/install//` sudo apt-get --yes purge `dpkg --get-selections | grep -v deinstall | grep alsa | sed s/install//` sudo apt-get --yes purge `dpkg --get-selections | grep -v deinstall | grep qt | sed s/install//` sudo apt-get --yes purge gcc-4.5-base:armhf gcc-4.6 gcc-4.6-base:armhf sudo apt-get --yes purge debian-reference libraspberrypi-doc raspberrypi-artwork gnome-themes-standard-data sudo apt-get --yes --purge autoremove
After running the above commands, we should see something like this from df -h:
Filesystem Size Used Avail Use% Mounted on rootfs 1.8G 577M 1.1G 35% /
Now that’s better. I’m sure there are numerous other things that can be removed, so if you know of something that could make a difference, drop me a line.
Update the System Software (RPi)
After significantly reducing the size of our system, it’s time to bring the remaining software up to date. This is done by a very handy script called rpi-update, which we have to download from the Internet. Here are the commands (sudo ./update.sh as a script) to download the script, run it, and reboot the system.
sudo apt-get update sudo apt-get --yes upgrade sudo apt-get --yes install binutils ca-certificates git-core sudo wget http://goo.gl/1BOfJ -O /usr/bin/rpi-update sudo chmod +x /usr/bin/rpi-update sudo rpi-update sudo reboot
Once you have completed the above and rebooted the system, you’ll notice that the system has a lot less free space.
Filesystem Size Used Avail Use% Mounted on rootfs 1.8G 852M 821M 51% /
That’s because, while rpi-update is very handy and does a lot of work for us, it downloads a lot of stuff from the Internet and doesn’t clean up after itself at all. So here are some simple commands (sudo ./update-clean.sh as a script) to do that cleanup:
sudo rm -rf /opt /boot.bak /lib/modules.bak /lib/modules/3.2.27-cutdown+/ /lib/modules/3.2.27+ /root/.rpi-firmware sudo apt-get --yes purge binutils git-core sudo apt-get --yes --purge autoremove
After removing all the extra stuff that rpi-update produced, we’re now back down to a more reasonable amount of free space:
Filesystem Size Used Avail Use% Mounted on rootfs 1.8G 608M 1.1G 37% /
Clean Up (RPi)
Even though we now have a clean, updated, headless system, there are still some things we can do to prepare the system for taking a snapshot. Specifically, we can install the sfill utility, remove the apt-get cache, clear (fill with 0’s) the empty file system space, clear the swap file, delete the log files, and shut down the device. Here are those commands (sudo ./clean.sh as a script):
sudo apt-get --yes install secure-delete sudo apt-get --purge autoremove sudo apt-get clean sudo sfill -f -ll -z / sudo swapoff -a sudo dd if=/dev/zero of=/var/swap bs=1M count=100 sudo swapon -a sudo rm `find /var/log -type f` sudo shutdown -h now
After doing all that, the final info from df -h should be something close to:
Filesystem Size Used Avail Use% Mounted on rootfs 1.8G 510M 1.2G 31% /
Create Disk Image (Mac)
And now, a couple of hours later, you have an SD card containing a headless RPi OS, and it’s time to make a disk image from it. The first step is to copy the contents of the SD card into a file on your computer, pretty much the exact opposite of burning the original OS onto the SD card:
- insert the SD card containing the headless OS
- use diskutil list to get the SD card’s device (usually /dev/disk1)
- copy the contents of the SD card into a file
diskutil unmountDisk /dev/disk1 dd if=/dev/disk1 of=rpi-headless-os.img bs=1m diskutil eject /dev/disk1
- remove the SD card, we’re done with it
That gives you a disk image the same size as the SD card, probably 2GB. Fortunately, most of that 2GB is empty file system space that we filled with zeroes (cleared) in the cleanup step above, so the file is ripe for compression. Running 7ZIP with maximum compression will produce a compressed disk image that’s only about 110MB in size.
7za a -t7z -mx9 rpi-headless-os.7z rpi-headless-os.img