Skip to content
Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Pi-router is a tool used to generate a custom Raspberry Pi OS image that transforms an RPi board into a secure router(strictly speaking an access point). Pi-router is derived from pi-gen and is based on 2020-12-02 release.

The resultant Pi OS can be considered an alternative to OpenWrt for RPi boards. It is secured with nftables and dnscrypt-proxy.


  • Grab the binary package from here and unzip it.

    $ unzip
  • Dump the image on an sd card.

    $ sudo dd if=2021-06-29-pirouter.img of=/dev/mmcblk0 bs=4M status=progress conv=fdatasync
  • Mount the sd card and change the two letter country code from fi to your home country in /etc/hostapd/hostapd.conf.

  • Unmount and take the sd card out.

  • Insert the card into sd card slot of RPi board and boot. Login password is Ra5pb3rry.

  • Optional: It might be helpful to read the section Network Configuration Details.


Pi-router build is tested with Debian buster, Ubuntu Focal Fossa, and Fedora 33.

To install the required dependencies for pi-router on Debian buster, run:

$ apt-get install coreutils quilt parted qemu-user-static debootstrap zerofree \
zip dosfstools bsdtar libcap2-bin grep rsync xz-utils file git curl bc

To achieve the same on Ubuntu Focal Fossa, replace the bsdtar package with libarchive-tools.

Similarly, on Fedora 33, run:

$ dnf install coreutils quilt parted qemu-user-static debootstrap zerofree \
zip dosfstools bsdtar libcap grep rsync xz file git curl bc

Other distributions should work but not tested. Feel free to give pi-router a spin on your favorite distro and let me know the results.


Upon execution, will source the file router-config in the current working directory. This bash shell fragment is intended to set needed environment variables.

The following environment variables are supported:


    Date on which the image is built.


    Combination of IMG_DATE and IMG_NAME(defined in router-config).

  • ZIP_FILENAME (Default: unset)

    IMG_FILENAME prefixed with image_.

  • RELEASE (Default: buster)

    The release version to build image against. Valid values are jessie, stretch, buster, bullseye, and testing. Note that pi-router is tested with buster only.

  • APT_PROXY (Default: unset)

    If you need to use apt proxy, set it here. The proxy setting will not be included in the image, making it safe to use an apt-cacher or similar package for development.

    If Docker is installed, it is possible to set up a local apt caching proxy to speed up subsequent builds like this:

    $ docker-compose up -d
    $ echo 'APT_PROXY=' >> router-config
  • BASE_DIR (Default location of

    This is the top-level directory for pi-router. It contains stage directories, build scripts, and by default both work and deploy directories. Changing this variable is not recommended.


    Directory containing helper scripts.

  • WORK_DIR (Default: "$BASE_DIR/work")

    Directory in which pi-router builds the target system. This value can be changed if a sufficiently large, fast, storage location is available for running build stages and caching purposes. It is important to note that WORK_DIR stores a complete copy of the target system for each build stage and it can grow extremely rapidly. This will result in hogging the storage space quickly. If you are building frequently, periodic cleaning of this directory is recommended.

  • DEPLOY_DIR (Default: "$BASE_DIR/deploy")

    Output directory for target system image.

  • DEPLOY_ZIP (Default: 1)

    By default, the image is deployed only as a compressed zip archive.

  • LOG_FILE (Default: "$WORK_DIR/build.log")

  • ENABLE_DEBUG (Default: unset)

    Sets the UART connectivity for debugging purposes

  • TARGET_HOSTNAME (Default: rpirouter)

  • FIRST_USER_NAME (Default: pi)

    User name for the first user

  • FIRST_USER_PASS (Default: Ra5pb3rry)

    Password for the first user.

  • WPA_ESSID (Default: pisecrouter)

  • WPA_PASSWORD (Default: 3T01F24h15h~)

  • WPA_COUNTRY (Default: fi)

    This value should be changed with the corresponding two letters country code.

  • ENABLE_SSH (Default: 1)

    SSH is enabled by default.

  • LOCALE_DEFAULT (Default: English("US"))

  • KEYBOARD_MAP (Default: us)

  • KEYBOARD_LAYOUT (Default: "English (US)")

  • TIMEZONE_DEFAULT (Default: "Europe/Helsinki")

  • STAGE_LIST (Default: stage*)

    If set, then instead of working through the numeric stages in order, this list will be followed. For example, setting to "stage0 stage1 mystage stage2" will run the contents of "mystage" before "stage2". Note the quotes, they are needed around the list. An absolute or relative path can be given for stages outside the pi-router directory.

A minimal router-config file is included in the build and is the default for needed, this file can be customized further.

Build Process

The image is built with the following process:

  • Loop through all of the stage directories in alphanumeric order

  • Run the script which is generally just used to copy the build directory between stages. In each stage directory loop through each subdirectory and then run each of the install scripts it contains, again in alphanumeric order. These need to be named with a two digit padded number at the beginning. There are a number of different files and directories which can be used to control different parts of the build process:

    • An executable unix shell script.
    • An executable unix shell script that will run in the chroot of the image build directory.
    • 00-debconf- Contents of this file are passed to debconf-set-selections to configure things like locale, etc.
    • 00-packages- A list of packages to be installed.
    • 00-packages-nr- As 00-packages, except these will be installed using the --no-install-recommends -y parameter to apt-get.
    • 00-patches- A directory containing patch files to be applied, using quilt. If a file named EDIT is present in the directory, the build process will be interrupted with a bash session, allowing an opportunity to create/revise the patches.
  • If the stage directory contains a file called EXPORT_IMAGE then add this stage to a list of images to generate. This is relevant for stage2 only as no image is exported for stage0 or stage1.

  • Generate the image for the stages with the above file; in this case, for stage2 only.

  • If the build process is interrupted, it is possible to start from the point of interruption by setting CONTINUE=1, e.g.

    $ CONTINUE=1 ./

Please refer to for finer details.

Docker Build

Docker can be used to perform the build inside a container. This partially isolates the build from the host system, and allows using the script on distributions other than Debian or Fedora. It might be worth noting that Docker build can be used on Debian or Fedora as well. Running Docker build is as simple as issuing the command below:

$ ./

If everything goes well, the final image will be in deploy/ directory. The build container can be removed after the build with the command:

$ docker rm -v pirouter_work

Similar to, can be continued from where it left during an interruption:


In case of a failure, the container can be examined by issuing the following command:

$ sudo docker run -it --privileged --volumes-from=pirouter_work pi-router /bin/bash

In case of successful build, the build container is by default removed. This can be changed by issuing the command:


Stage Anatomy

The build process is divided up into several stages for logical clarity and modularity. This causes some initial complexity, but it simplifies maintenance and allows for more easy customization.

Stage 0 - bootstrap. The primary purpose of this stage is to create a usable filesystem. This is accomplished largely through the use of debootstrap, which creates a minimal filesystem suitable for use as a base.tgz on Debian systems. This stage also configures apt settings and installs raspberrypi-bootloader which is missed by debootstrap. The minimal core is installed but not configured, and the system will not quite boot yet.

Stage 1 - truly minimal system. This stage makes the system bootable by installing system files like /etc/fstab, configures the bootloader, makes the network operable, and installs packages like raspi-config. At this stage the system should boot to a local console from which you have the means to perform basic tasks needed to configure and install the system. This is as minimal as a system can possibly get, and its arguably not really usable in a traditional sense yet. Still, if you want minimal, this is minimal and the rest you could reasonably do yourself as sysadmin.

Stage 2 - router system. This stage produces the router image. It installs some optimized memory functions, sets timezone and charmap defaults, installs fake-hwclock and ntp, wireless LAN and bluetooth support, dphys-swapfile, and other basics for managing the hardware. It also creates necessary groups and gives the pi user access to sudo and the standard console hardware permission groups.

All the customizations needed to transform RPi into a secure router are done at this stage. Contrary to pi-gen build stages, pi-router does not need to go beyond this stage.

Network Configuration Details

Pi-router makes use of dnsmasq, dhcpcd, and hostapd, to transform the RPi into a router. The default LAN side gateway ip address is and the connected clients are assigned addresses in the range of WAN side address is supplied by the RJ-45 connector on RPi board.

Known Limitations

  • Pi-router does not handle the case of WAN side address supplied by a USB to RJ-45 dongle plugged into one of the four RPi USB ports supports WAN side address supplied by a USB to RJ-45 dongle with certain limitations. The support is limited to those ethernet to USB adapters whose drivers are built into the kernel and is available in mak-dev branch. In order to test, define (usually eth1) WAN_INTERFACE in router-config and run the build script. It is important to keep in mind that the support is of alpha quality.

  • DHCPv6 support is experimental. In case there is no valid global IPv6 address assigned to WAN interface, dhcpv6-client service will fail.



Linux is able execute binaries from other architectures, meaning that it should be possible to make use of pi-router on an x86_64 system, even though it will be running ARM binaries. This requires support from the binfmt_misc kernel module.

You may see the following error:

update-binfmts: warning: Could not load the binfmt_misc module.

To resolve this, make sure that binfmt_misc module is loaded and qemu-arm-static binary is available.

$ lsmod | grep binfmt_misc
$ command -v qemu-arm-static

If you find this work useful, consider buying me a coffee.