Published in


Installing 64-bit Fedora on the Raspberry Pi 4

Hacker; noun. One who makes furniture with an ax.

This is a hack.

This is a hack because as of the writing of this piece, we have two excellent halves of a solution, and I just need something that works.

On one hand, the Raspberry Pi Foundation have

  • A pretty remarkable 1.5GHz quad-core, 4GB memory, 64 bit computer with enough stuff on the motherboard to qualify for a desktop, in a $50 package, called the Raspberry Pi 4b.

On the other hand, we have the Fedora project. An enterprise-grade operating system user-space built natively for 64 bit arm, with a massive catalogue of enterprise software, some Seriously Cool Stuff that can be built off it (I’m looking you right in the eye, Kubernetes+etcd straight from the OS repos). Sadly, Fedora does not currently support the Pi 4b, its u-boot boot loader doesn’t fit the Pi 4b like a glove, and the kernels as of this moment do not run stable on the Pi 4b model or support some of its hardware.

This guide will show how to put the above together in minimally working order. Because I need to repeat this a lot, I cut some Ansible code to do it all.

Some prep work first:
We will need one or more running PCs to fulfil the following roles (I assume you are running Fedora or a derived OS; it will work on Debian/Ubuntu just as easily, but you’ll need your nerd license to adapt it a bit):

  1. base: A place to run some commands (an Ansible playbook).

The same machine can be all three (but you can also use AWX/Tower for base, a virtual machine for builder1 and a Fedora laptop for flasher1, if you’re thus inclined. It doesn’t really matter.)

  1. Install ansible and git:
[mikishapiro@base ~]$ sudo dnf -y install ansible git

2. Grab the code:

[mikishapiro@base ~]$ git clone

3. Modify your default Ansible inventory file (armbuild/inventory), with the settings you want.

This includes naming which machine(s) will fill the builder1 and flasher1 roles, some configuration for the resulting Raspberry Pi Fedora image and so forth.

You will also need to plug your SD card in the flasher1 machine, run

[mikishapiro@flasher1 ~]$ sudo lsblk

… and figure out from the output what device it is. It’ll probably be a /dev/mmcblk* device if it’s sitting in an onboard SD card reader, or a /dev/sd* device if it’s sitting in a USB adapter. I use a USB adapter, and it appears as /dev/sdb on my system.

4. We’ll need to set up a bit of ssh access to allow Ansible to do the heavy lifting for us.

Ensure your user is in the ‘wheel’ group and can sudo to root on builder1/flasher1. To simplify this writeup, I will assume you can sudo to root without needing to provide a password (uncomment the %wheel line containing NOPASSWD in /etc/sudoers and comment the original one to achieve this).

5. Ensure your user on base and builder1 has an ssh key generated. Run ‘ssh-keygen’ command and create one with a blank password if not.

6. Ensure your user on base can ssh to builder and to flasher by logging on to base and typing

[mikishapiro@base ~]$ ssh-copy-id builder1
[mikishapiro@base ~]$ ssh-copy-id flasher1

Also, you will need to ensure your user on builder1 can ssh to root@flasher1.

builder$ ssh-copy-id root@flasher1

That should be it.

Note: If you use any of this in any kind of production environment, you will need to make this method more secure, by storing sensitive configuration items (like password hashes or a public ssh key) in Ansible Vault (or AWX/Tower), or by avoiding the ssh to root operations I use. This is outside the scope of this writeup.

That’s the prep. Now just run the deploy.yml playbook:

[mikishapiro@base ~]$ cd armbuild[mikishapiro@base armbuild]$ ansible-playbook -i inventory deploy.yml

You can add -vvv to the second command if you like gory debug.

If everything is in order, that should be it. Some 10–15 minutes after the downloads are complete, you should have a flashed SD card (assuming USB2 transfer speeds). Transfer your SD card to your Raspie and power it up.

Re-running the playbook (with same or different SD card) will not re-perform the lengthy download operations if not necessary, and if you use the lazy=true configuration item in the inventory, it’ll recycle the existing unarchived images as well, saving more time.

Go ahead and have a look at the Ansible code to see what it does — the core logic is in the deploy.yml playbook, while some of the low level operations are inside the arm-build role, in the tasks/*.yml files related to the instructions it knows how to handle.

I’ve tested this using a pre-release build of Fedora Server raw-xz (aarch64) version 32 from here: and Raspbian Lite from here:

In a nutshell, the playbook (running on base) will

  1. Download and unarchive both OS images (to builder1), but without deleting the archives. There’s a bit of acrobatics in there for the Raspbian image to resolve its redirect URL, find out the real filename and save the image with the correct filename. This saves re-downloads.

I hope you found this helpful.

In my next write-ups, I will be covering

  • How to natively compile a kernel directly on the aarch64 Fedora v32 system produced above (instead of cross-compiling or using an emulator), using the kernel source tree provided by the Raspberry Pi foundation (which contains full support for the Pi 4b features), and with the fancy stuff we will need for OKD and COSA.

Have fun, and don’t forget to be awesome :)



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store