Setting up a Headless Raspberry Pi Zero

Most of the time when I use a Raspberry Pi, I’m using them ‘headless.’ Either the boards are embedded inside projects, or hidden away in a cupboard. While they’re connected to the network, there isn’t a monitor, keyboard, or mouse to be seen anywhere. However what seems to surprise a lot of people is that setting them up like that is actually now pretty easy.

Getting the Operating System

Go ahead and download the latest SD Card image of Raspbian, or if you don’t intend to ever use the Raspberry Pi with a monitor—or connect to it using VNC—of Raspbian Lite.

For burning card images these days I’d generally recommend Etcher, made by the folks at It’s cross platform — it works on Windows, Linux and mac OS — and lets you burn an image in four clicks.

Burning the aiyprojects-2018–01–03.img.xz card image using Etcher.

However, if you’re a command line person like me, you can either download and install the experimental Etcher command line tools, or you can still go ahead and do it the old way.

The instructions here are for the Mac, because that’s what I have on my desk, but instructions for Linux are similar.

Go ahead and insert the micro SD card into the adaptor, and then the card and the adaptor into your Macbook. Then open up a Terminal window and type df -h, and check the device name for your SD Card. In my case it’s /dev/disk1, and I’ll need to use the corresponding raw device, /dev/rdisk1, when writing to the card.

Go ahead and unmount the card from the command line,

$ sudo diskutil unmount /dev/disk1s1

rather than ejecting it by dragging it to the trash. Then from there we can go ahead and write the image to our SD card.

Unfortunately the card image came as a .xz file and there isn’t a command line tool to uncompress these sorts of file available by default on macOS. Fortunately, if you have Homebrew installed, you can brew install the xz command line tool. In the Terminal window change to the directory with your downloaded disk image and type,

$ brew install xz
$ xz -d aiyprojects-2018-01-03.img.xz

to uncompress the disk image, and then write it to your card as follows,

$ sudo dd bs=1m if=aiyprojects-2018-01-03.img of=/dev/rdisk1

If the above command reports an error dd: bs: illegal numeric value, change bs=1m to bs=1M.

The image’s boot partition should be automatically remounted after dd is done writing the image.

Enabling Wireless Networking

If you’ve used Etcher, or if the card’s boot partition hasn’t automatically been remounted, you’ll need to open Disk Utility and remount the boot partition. Alternatively you can just pull the card out, and reinsert it, which is probably easier, which should also mount the boot partition automatically.

Make sure the partition is mounted and navigate to the boot partition, and create a new file named wpa_supplicant.conf using your favourite editor,

$ cd /Volumes/boot
$ nano wpa_supplicant.conf

and add the following lines,

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev

Where COUNTRY should be set the two letter ISO/IEC alpha2 code for the country in which you are using your Pi, e.g.

  • GB (United Kingdom)
  • FR (France)
  • DE (Germany)
  • US (United States)
  • SE (Sweden)

and SSID is the ESSID of your home network, with PASSWORD being the WPA2 password for that network.

It’s important to enter the correct country code in the file as this will determine which regulatory domain your Raspberry Pi thinks its operating in, and therefore which wireless channels it enables on your adaptor.

Enabling SSH

Recent releases of the Raspbian operating system have the SSH server disabled on boot, and since we’re intending to run the board without a monitor or keyboard, we need to renable it if we want to be able to SSH into our Raspberry Pi. You can do this by making sure there is a file called sshpresent in the boot volume. Go ahead and enter,

$ touch ssh

at the command line. When the Pi first boots, it looks for this file; if it finds it, it will enable SSH and then delete the file. The contents of the ssh file don’t matter.

Enabling OTG

One disadvantage of Raspberry Pi Zero compared to a normal Raspberry Pi is the lack of Ethernet port. That means we’re relying on wireless networking to give us remote access to our Pi, unfortunately we need to configure the board for every wireless network we want to use. So it’s handy to have another route to log into the Pi.

Fortunately we can access our Raspberry Pi fairly easily using something called USB OTG which will allow us to set up a virtual network connection between your Raspberry Pi Zero and our laptop. This will allow you to SSH over the USB cable powering the Pi Zero, allowing us to configure wireless networking without need of a keyboard, mouse, or screen.

Make sure the boot partition is still mounted, and go ahead and open the config.txt file in an editor of your choice, e.g.

$ nano config.txt

and make sure it contains the following entry,


which may well be already appended near the bottom of the file. However if not, add it. Next, go ahead and edit the cmdline.txt file.

$ nano cmdline.txt

You need to be careful here, as the formatting of this file is pretty important. Each parameter should be separated by a single space, not a newline or a tab. Go ahead and insert modules-load=dwc2,g_ether after rootwait.

Initially it should look like this,

$ cat cmdline.txt
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=020c3677–02 rootfstype=ext4 elevator=deadline rootwait quiet init=/usr/lib/raspi-config/ splash plymouth.ignore-serial-consoles

and afterwards it should look like this,

$ cat cmdline.txt
dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=PARTUUID=020c3677–02 rootfstype=ext4 elevator=deadline rootwait modules-load=dwc2,g_ether quiet init=/usr/lib/raspi-config/ splash plymouth.ignore-serial-consoles

Once you’re done, eject the card with the command,

$ sudo diskutil eject /dev/rdisk1

and you should have a working card image with all three of wireless networking, SSH, and OTG now enabled.

Testing our OTG connection to the Raspberry Pi

Go ahead and insert your micro SD card into your Raspberry Pi Zero and connect it via USB to your computer. It is important to connect your laptop to the Raspberry Pi Zero using the USB, rather than the PWR, micro-USB port.

Connect your Raspberry Pi to your laptop using the USB, rather than the PWR micro USB port.

Using the USB port will power the board, but more importantly it will also allow us to make a data connection. After connecting the Pi Zero to your laptop the green ACT LED should start flashing. It could take as long as 90 s to boot up the first time, although it should be shorter on subsequent boots.

The Raspberry Pi will be listed as ‘RNDIS/Ethernet Gadget’ in Settings.

After it has finished booting the connection to the Raspberry Pi should appear as a USB Ethernet device called RNDIS/Ethernet Gadget in the Settings app. It’s there that we can see the IP range being used by the USB connection.

Typically the IP address range on your home network will use the 192.168.1.* IP block, but our laptop USB Ethernet connection has been given an address in the 192.168.11.* block.

While we know the IP address of our laptop, in this case, we don’t yet know the IP address of the Pi. The easiest way to find this out is to use nmap, an open source utility for network discovery. This doesn’t come installed by default on macOS, however you can easily download a disk image file containing the installer and follow the prompts to get up and running.

Once installed you should run it as follows,

$ nmap -sn
Starting Nmap 7.50 ( ) at 2018-01-18 07:11 GMT
Nmap scan report for
Host is up (0.0032s latency).
Nmap scan report for
Host is up (0.0021s latency).
Nmap done: 256 IP addresses (2 hosts up) scanned in 18.95 seconds

Here we can see there are two hosts in the 192.168.11.* range. Our laptop, and an unidentified host that has to be our Raspberry Pi.

Alternatively we can use the dns-sd command, which will work even if we don’t have Internet Sharing turned on for the RNDIS/Ethernet Gadget device and we end up with a Self-Assigned IP address for the connection.

$ dns-sd -G v4 raspberrypi.local
DATE: ---Mon 05 Mar 2018---
21:18:28.163 ...STARTING...
Timestamp A/R Flags if Hostname Address TTL
21:18:28.163 Add 3 26 raspberrypi.local. 120
21:18:28.164 Add 3 4 raspberrypi.local. 120
21:18:28.164 Add 2 27 raspberrypi.local. 120

We can check the connection with a quick ping request,

$ ping
PING ( 56 data bytes
64 bytes from icmp_seq=0 ttl=64 time=0.093 ms
64 bytes from icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from icmp_seq=2 ttl=64 time=0.043 ms
--- ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.043/0.062/0.093/0.022 ms

and once we’ve found the Pi, you can go ahead and login with ssh—the default username and password are “pi” and “raspberry” respectively.

$ ssh pi@
The authenticity of host ' (' can't be established.
ECDSA key fingerprint is SHA256:53IF2C4ji0MdCfjQkhLLVy6ETEkL3fErkELG2tqgmuc.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '' (ECDSA) to the list of known hosts.
pi@'s password:

After logging in we can also check that our Raspberry Pi has successfully logged on to our local wireless network using ifconfig.

$ ifconfig wlan0
wlan0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet netmask broadcast
inet6 fe80::e6bd:f82d:4fac:e6ea prefixlen 64 scopeid 0x20<link>
ether b8:27:eb:ab:13:e1 txqueuelen 1000 (Ethernet)
RX packets 841 bytes 229952 (224.5 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 182 bytes 27620 (26.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

With wireless networking enabled and working you won’t need to connect it to your laptop again unless something goes wrong.

The Raspberry Pi connected to a USB power supply.

Instead, you can plug the Pi directly into a power supply and after it finishes booting the Raspberry Pi should advertise itself using mDNS with a default name of raspberrypi.local, allowing you to find it easily on the network.

At this stage you should shut down your Raspberry Pi, detach it from your laptop, plug it into your power supply in the normal way, and wait for it to boot.

Installing a VNC Sever

We can now go ahead and install a VNC server. This is an optional step, but is quite useful if you want to be able to get to your Raspberry Pi desktop over the network.

Go ahead and SSH back into your Pi, this time over your wireless network,

$ ssh pi@raspberrypi.local

Then type,

$ sudo apt-get update
$ sudo apt-get install realvnc-vnc-server realvnc-vnc-viewer

to install the server. Once it’s installed we can enable it using the Raspbian configuration utility. Type,

$ sudo raspi-config

at the prompt to open the configuration manager. Using the Up/Down cursor keys navigate to Interfacing Options and the Enter key to select it. Then scroll down and select VNC, and then answer Yes when prompted. This will turn on the server, and return you to the main menu.

Now navigate to Advanced Options, then select Resolutions, and pick a workable resolution, I generally go with 1600×1200 as it fits nicely on my Mac’s desktop.

We need to do this because — as we’re connecting to a headless Pi — the VNC server will default to the smallest safe resolution, typically the same as a standard definition TV, which isn’t going to be particularly usable.

Then use the Left/Right cursor keys to navigate to Finish and hit the Enter key. You’ll be asked whether you want to reboot now, answer Yes.

Connecting to the Raspberry Pi using the Real VNC Viewer application.

Once the Pi has rebooted you should log back in as before using ssh to make sure everything is working correctly.

Unfortunately the version of VNC that we’re now running on the Pi isn’t compatible with the built in screen sharing on macOS. However RealVNC offers a VNC Viewer applicationfor Windows, Linux, and macOS — as well as a number of other platforms.

So go ahead and download the application and install it on your laptop.

Once installed you should now just be able to again connect directly to raspberrypi.local with the default username and password.

If everything has worked, you should see the Raspberry Pi desktop in a window. That’s it. You’ve got your Raspberry Pi configured and working, ready to be used in headless mode in your project.