Skip to main content

Raspberry Pi Remotely Right Away

ยท 9 min read
Dominik Nowak

In one of the previous blog posts I presented how to configure a Raspberry Pi to access it from anywhere without public IP or port forwarding on your router. It's quite easy, but still you need to make a few steps manually, like providing your Wi-Fi credentials, installing the Husarnet VPN Client (recently open sourced), and typing commands in the terminal to connect the board to your Husarnet VPN network.

If you have to confiture a single Raspberry Pi that might be a fun way of spending a time, but what if you would like to connect a dozen(s) of them? My math teacher in elementary school often said that "a good mathematician should be lazy". That's also a true sentence for engineers - if you can automate something that is repetitive and boring - do it.

In this blog post I'll show you how to create your own, customized system image to be burned on the SD card of your Pi, so that after you power on your board, it will be available via SSH over the Internet on the 1st boot! In the same way you can generate hundreds of similar images, without wasting your time to manually setup each board.

Cover image

Quick Startโ€‹

Here's a quick summary of what to do in order to prepare and access your RPi on 1st boot...

  1. ๐Ÿ’พ Go to template project and click [Use this template] button to make it available on your own GitHub profile. Remember to make your repo based on this template private!

  2. ๐Ÿ” Choose whether you would like to build a system image using GitHub Actions workflow or on your laptop. If you have chosen GitHub Actions, just define the following repository secrets:

  3. ๐Ÿš€ Trigger the GitHub Actions workflow and find a ready to use image in build artifacts.

  4. ๐Ÿ”ฅ Now burn the image on SD card the same way as with any other images for Raspberry Pi (by using tools like Etcher or Raspberry Pi Imager (rpi-imager))

  5. ๐Ÿ”Œ Insert an SD card into your Raspberry Pi and power it on.

  6. ๐ŸŒŽ In under a minute you should be able to access your Pi from anywhere, by using it's hostname:

    ssh ubuntu@my-raspi

...but if you want to learn more, continue reading :)


I have created a boilerplate GitHub repository that automatically builds the system image based on the official Ubuntu 20.04 Server image for Raspberry Pi. This is a template repository, so you can copy that template directly to your own GitHub profile and customize anything you want.

The project utilises Hashicorp Packer and packer-builder-arm plugin. That software suite allows you to customize existing system images for different cloud vendors, virtual machines etc. Building custom images for ARM64 is supported by the 3rd party plugin for Packer.

What the Template Containsโ€‹

In the project template the following customizations are made based on the official Ubuntu 20.04 Server image:

  1. Setting up Netplan config for the most common use case - connecting to WPA secured Wi-Fi hotspot.

  2. Providing Wi-Fi credentials through environment variables.

  3. Installing Husarnet VPN Client.

  4. Creating a systemd service to connect your board to the VPN network as soon as it has internet access (by using husarnet join $JOINCODE $HOSTNAME).

  5. Installing Docker & Docker-Compose - as an extra step and only to demonstrate you how easily you can install whatever you want before even booting your Pi the first time.

OK, what to do next?โ€‹

Using the Templateโ€‹

Go to the project repo on GitHub: and click the [ Use this template ] button.

Click use this template button

In the next window choose a name of your own repository (eg. my-own-pi-img) based on the template. In most cases it's better to make this repository Private, because inside it's "build artifacts" tab you will have your own system image ready to be downloaded with Wi-Fi and Husarnet credentials, which are sensitive information.

Finally click the [Create repository from template] button. After a few seconds you will have a new repository ready on your GitHub profile.

Building the Image...โ€‹

...on Your Local Machineโ€‹

The project is based on Docker, so if you want to build the project locally, you need to install Docker and Docker Compose first. Clone the repo, find configs.pkrvars.hcl.template file and rename it to configs.pkrvars.hcl.

# Place your Husarnet JoinCode and your hostname here
# You will find your JoinCode at
# -> Click on the desired network
# -> [Add element] button
# -> [Join code] tab

husarnet_hostname = "my-remote-rpi"
husarnet_joincode = "fc94:b01d:1803:8dd8:b293:5c7d:7639:932a/xxxxxxxxxxxxxxxxxxxxxx"

wifi_ssid = "my-wifi-network"
wifi_pass = "password-to-my-wifi"

Place your Husarnet Join Code and Wi-Fi credentials here, and run:

docker-compose up --build

After a few minutes, your repo file structure will look more or less like this:

arnold@my-cool-laptop:~/my-own-pi-img$ tree
โ”œโ”€โ”€ configs.pkrvars.hcl
โ”œโ”€โ”€ docker-compose.yml
โ”œโ”€โ”€ Dockerfile
โ”œโ”€โ”€ img_files
โ”‚ โ”œโ”€โ”€ 01-netcfg.yaml
โ”‚ โ”œโ”€โ”€ 99-disable-network-config.cfg
โ”‚ โ”œโ”€โ”€
โ”‚ โ”œโ”€โ”€ join-husarnet.service
โ”‚ โ””โ”€โ”€
โ”œโ”€โ”€ LICENSE
โ”œโ”€โ”€ output
โ”‚ โ”œโ”€โ”€ rpi-ubuntu-20.04-server-20210919163734.img
โ”‚ โ”œโ”€โ”€ rpi-ubuntu-20.04-server-20210919163734.img.tar.gz
โ”‚ โ””โ”€โ”€ rpi-ubuntu-20.04-server-20210919163734-sha256.checksum
โ”œโ”€โ”€ packer_cache
โ”‚ โ”œโ”€โ”€ 7150b8f499be6e49dd9be40ebf9ebc358f61ab59.xz
โ”‚ โ””โ”€โ”€ 7150b8f499be6e49dd9be40ebf9ebc358f61ab59.xz.lock
โ”œโ”€โ”€ report.xml
โ””โ”€โ”€ ubuntu_server_20.04_arm64.pkr.hcl

3 directories, 17 files

There are two new folders:

  • output - with your customized system image (~3.4GB *.img file), compressed version (~1.1GB *.img.tar.gz file) and a checksum file.
  • packer_cache - thanks to that folder, the following image builds will be ready much faster (eg. after modifying the template)

You can now skip the section about using GitHub Actions to build the image and go directly to the next section.


By default output and packer_cache folders are owned by root user (even if you run docker-compose up --build as a normal user). So before re-running the build, change ownership of those folders:

sudo chown $USER:$USER output/ packer_cache/

If you prefer docker run ..., you will find a copy & paste ready commands for that in the repo's README

...using GitHub Actionsโ€‹

The template contains the ready to use workflow that allows you to build your customized Raspberry Pi image using GitHub Actions.

By default the workflow can be triggered manually or by git push event. Modify .github/workflows/build_rpi_image.yaml to change that behaviour.

Start by defining the following repository secrets for your repo (please go to [Settings menu -> Secrets tab -> [ New repository secret ] button] ):


After you finish it should look like that:

repository secrets for GitHub Actions

Now go to Actions menu in your repo, click the [Select workflow] button and select Build custom RPi images option. You should be able to trigger the workflow with a [Run workflow] button:

Manually triggering the GitHub Actions workflow

When the process is finished, you should be able to find the compressed image for your Raspberry Pi in the Build artifacts under Actions menu. Download it for the next steps.

Build artifacts from GitHub Actions


The workflow allows you also to save the newly built image on Amazon S3 bucket. To enable that feature, just define 3 new repository secrets:


Read more in README.

Burning the Image on SD Cardโ€‹

  1. Format the SD card (with ext4)

  2. Extract *.img.tar.gz to *.img, eg.:

    sudo tar -xf rpi-ubuntu-20.04-server-<timestamp>.img.tar.gz
  3. Now burn that rpi-ubuntu-20.04-server-<timestamp>.img image file on SD card using tools like Etcher or Raspberry Pi Imager (rpi-imager)

Booting & Accessing your Raspberry Piโ€‹

Place the SD card in the SD slot of your Pi and power it on. After a while you should be able to see your Raspberry Pi available in your Husarnet VPN network dashboard at

You can now access your Raspberry Pi over the internet from any device that is in the same Husarnet network by using:

ssh ubuntu@my-remote-rpi

How Does It Workโ€‹

The magic happens in the ubuntu_server_20.04_arm64.pkr.hcl file, which is an input for Hashicorp Packer.

ubuntu_server_20.04_arm64.pkr.hcl (fragments)

locals {
my_timestamp = regex_replace(timestamp(), "[- TZ:]","")
image_name = "rpi-ubuntu-20.04-server"
output_path = "./output"

variable "husarnet_hostname" {
type = string
default = "packer-test"
source "arm" "ubuntu" {
file_urls = [""]
file_checksum_url = ""

build {
sources = ["source.arm.ubuntu"]
provisioner "shell" {
inline = [
# Installing Husarnet
"curl | sudo bash",

# Join Husarnet network with a Join Code on system boot
"sudo touch /etc/husarnet-credentials",
"echo 'HUSARNET_HOSTNAME=${var.husarnet_hostname}' >> /etc/husarnet-credentials",
"echo 'HUSARNET_JOINCODE=${var.husarnet_joincode}' >> /etc/husarnet-credentials",
"chmod +xr /usr/local/bin/",
"sudo systemctl enable join-husarnet.service",

post-processor "compress" {
output = "${local.output_path}/${local.image_name}-${local.my_timestamp}.img.tar.gz"

We specify here a link to the official Ubuntu 20.04 Server image for Raspberry Pi and then, in build section we use so-called provisioners to perform system modifications like: executing shell commands, copying files etc. Basically you type commands here that you would normally type in the Linux terminal. *.pkr.hcl is like Dockerfile, but for bare metal system images instead of Docker images.

Adding your own software or configuration to the system image is simple. Thanks to this template you don't even need to learn Packer - just do your own configurations by analogy to mine.


Hashicorp Packer is a great tool allowing you to save a lot of time on boring tasks like 1st configuration of Raspberry Pi.

In the article I introduced a GitHub template project to build your own system image in minutes and make it available over the Internet on first boot!

Raspberry Pi headless setup is now extremely easy and you can access it remotely right away after powering it up!

Read also ...โ€‹