Setting up a headless Raspberry Pi the hard way

Dragan Čečavac
5 min readOct 7, 2023

--

The most straight forward way to configure your Raspberry Pi is to use the official rpi-imager. I would recommend it to most people, as image flashing, enabling SSH and bringing up WiFi takes just a few clicks. Howeverm maybe rpi-image crashes on your system (welcome to the club) or you perhaps just don’t want to install additional software on your system for this purpose. Maybe you also don’t have the specific USB/HDMI adapters needed to access the board directly. In any case here’s a step-by-step guide on how to get it done on a Linux system.

Image selection

First thing to do is to select an appropriate system image for your Raspberry Pi. Official images with listed support models are available at https://www.raspberrypi.com/software/operating-systems/. As this is a headless setup, you won’t be needing desktop environment, so just go for a Pi OS Lite version.
After downloading the image archive you can extract it using your Archive Manager or just run unxz <archive-file> in the terminal. In my case it was unxz 2023-05-03-raspios-bullseye-armhf-lite.img.xz which lead to creation of 2023-05-03-raspios-bullseye-armhf-lite.img.

Image flashing

Before listing any more of terminal commands I would recommend using image flashing tools like Balena Etcher, if you are more comfortable with this approach. The end result should be the same.

We will have to flash the image to an SD card and this step may depending on the system. You will need to determine what is the device path of the SD card. A simple way to do this is to run lsblk command in the terminal before and after inserting the card. In most cases it should be easy to spot the difference.
In my case the device name ended up being mmcbkl0 and its full path was /dev/mmcbkl0.

Some systems might auto-mount SD card partitions upon insertion, so it’s always safest to unmount or at least attempt to unmount all its partitions before flashing. This can be done by running sudo umount <device-path>*. * at the end is important and it will make sure that all of the partitions will be unmounted.

Don’t be afraid of unmount: <device-path>: not mounted. warnings. In this concrete example of mine, I ran the command twice just for demonstration and you can see that /dev/mmcblk0p1 actually was auto-mounted and got unmounted after the first command. The second command only reassured me that the first command was successful.
You could of course also check upfront if it is mounted by inspecting the output of mount command, but I believe that it’s simpler to just directly unmount, than checking mounted list and still having to unmount in the end.

Finally we can flash the image. Be sure to double-check the device path before running the command, if you have devices which have similar names, you might overwrite their contents accidentally.
Flashing can be done by running sudo dd if=<image-file> of=<device-path>. In my case it was sudo dd if=2023-05-03-raspios-bullseye-armhf-lite.img of=/dev/mmcblk0. The command might take a while to complete, and when it’s done I would suggest to also run sync to be sure that everything is fully flashed to the SD card.

Enabling WiFi

This step might not be needed if you plan to plug in Ethernet cable, but otherwise it might be essential for connectivity.

To enable WiFi, you should create a file named wpa_supplicant.conf in SD cards’s /boot partition and provide the following content:

country=<two-letter-country-code>
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network={
scan_ssid=1
ssid="<network-name>"
psk="<network-password>"
}

<two-letter-country-code> might be DEor US for example, depending on your location.

Enabling SSH

To enable SSH it’s enough to create a file named ssh in SD card’s /boot partition.
However, when using a recent Raspberry Pi image, this won’t be enough to actually login, as the password won’t be configured. Even using the default username pi with the default password raspberry will lead to a warning saying Please note that SSH may not work until a valid user has been set up..
To configure user’s password we can first generate a hashed version of the password using echo '<password>' | openssl passwd -6 -stdin. For the default one it would be echo 'raspberry' | openssl passwd -6 -stdin. This will always generate a different output, but in general should look like this:

This output should then be copied to etc/shadow file of the SD card’s rootfs partition, replacing * character in pi user’s row. Be sure to use sudo when starting your text editor, because file etc/shadow is a protected one for obvious reasons. After editing, it should end up looking similar to this one:

Logging in

At this point you may insert the SD card into your Raspberry Pi and boot it up. You might want to give it a bit more time on the first boot, but eventually you should be able to ping your device using ping <ip-address> and if you’re not sure about the address ping raspberrypi.local should work in most cases.
If you were able to ping your device, congratulations! This means that it successfully connected to WiFi.

Now you should be able to login using SSH by running ssh pi@<ip-address> or ssh pi@raspberrypi.local.

This is not essential, but you can avoid having to type in your password each time by running ssh-copy-id pi@<ip-address> or ssh-copy-id pi@raspberrypi.local.

That’s all, happy tinkering!

--

--

Dragan Čečavac

Software engineer interested in how things work in general. Mostly experienced with embedded Linux and Android.