Aug 15, 2020
Get started with Ubuntu 20.04 on Raspberry Pi 4
Background
With the advent of the Raspberry Pi 4, Pi’s have become quite powerful both in CPU and memory terms and are now good candidates for software development on ARM architecture. The Pi 4 boasts quad-core ARM v8 1.5Ghz CPU with 64-bit support and the option of 2, 4 or 8GB of DDR4 memory. It also has excellent connectivity through dual HDMI support (including 4k), Gigabit Ethernet and USB 3.0 (and 2.0) ports.
My personal interest in the Pi 4 is in part AWS-based. AWS has recently made significant headway in the ARM space with the release of Graviton-based EC2 instances based on ARM64 (aka. AArch64) architecture.
This post details how to set-up and get started with Ubuntu 20.04 on the Pi 4.
So why choose Ubuntu over Raspberry Pi OS (aka. Raspbian)?
The Raspberry Pi 4 has ARM64 64-bit (AArch64) support. With the Pi 4 the additional memory options (4GB and 8GB) makes it highly relevant to use a 64-bit OS.
Ubuntu has distributions that can be installed on the Raspberry Pi. This includes a 64-bit version of the OS for select Pi 3 and 4 models.
Currently the Raspberry Pi OS 64-bit version has only been announced in Beta recently.
On release, Ubuntu 20.04 was certified with full support on Raspberry Pi 4.
In other words, Ubuntu 20.04 is currently the only fully supported ARM64 64-bit OS.
Installation
There is a How to install Ubuntu on your Raspberry Pi tutorial available from Ubuntu, which is easy to follow.
Configuration
This section details the various configuration that is needed after successful installation of Ubuntu on the Raspberry Pi.
Screen resolution
Raspberry Pi screen resolution configuration - incuding custom modes - is documented in hdmi-config.md and video.md.
The file /boot/firmware/usercfg.txt
contains custom Ubuntu configuration for the Raspberry Pi. This includes things like screen resolution, which take effect immediately during boot.
Note that the configured screen resolution takes effect for both terminal and X window system.
Below is an example configuration for a custom 2560 x 1080 monitor configuration (hdmi_mode
87) with 21:9 (7) aspect ratio. The example will need to be adjusted according to monitor type and resolution & refresh requirements.
The example also uses the vc4-fkms-v3d driver, which provides acceleration for both 2D (e.g. desktop) and 3D (OpenGL ES 3). More information here.
dtoverlay=vc4-fkms-v3d
max_framebuffers=2
gpu_mem=128
#hdmi_enable_4kp60=1
# [HDMI:0]
hdmi_group:0=2
hdmi_cvt:0=2560 1080 60 7
hdmi_mode:0=87
hdmi_drive:0=2
disable_overscan:0=1
max_framebuffer_width:0=4096
max_framebuffer_height:0=2160
framebuffer_width:0=2560
framebuffer_height:0=1080
# [HDMI:1]
hdmi_group:1=2
hdmi_mode:1=82
hdmi_drive:1=2
After editing the file, do a reboot, e.g. using the sudo reboot
command.
Installing a desktop
The simple, lightweight desktop called LXQt can be installed by running: sudo apt install lubuntu-desktop
.
There are other options too.
Set hostname
A new hostname can be set, e.g. rpi4-1
, with the command: sudo hostnamectl set-hostname rpi4-1
.
Static network IP
Follow the Ubuntu server network configuration documentation.
Per default the wired (eth0) network adapter uses DHCP to get an IP address and is listed in the Ubuntu Server UI Desktop as being Unmanaged.
The eth0 DHCP network configuration exists in the cloud-init netplan file /etc/netplan/50-cloud-init.yaml
:
network:
ethernets:
eth0:
dhcp4: true
optional: true
version: 2
To disable the cloud-init netplan, which provides above configuration, create a new file /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
and paste in:
network: {config: disabled}
Create a new netplan configuration file /etc/netplan/99_config.yaml
with static IP address configuration, which resembles:
network:
version: 2
renderer: networkd
ethernets:
eth0:
addresses:
- 192.168.0.14/24
gateway4: 192.168.0.1
nameservers:
addresses:
- 192.168.0.1
- 1.1.1.1
Remove the cloud-init netplan file:
sudo rm /etc/netplan/50-cloud-init.yaml
Apply netplan configuration (or alternatively reboot):
sudo netplan apply
More advanced configurations can also be achieved with netplan. E.g. multiple networks, IP addresses and default routes with metric-based weighting. Here’s an example:
network:
version: 2
renderer: networkd
ethernets:
eth0:
addresses:
- 192.168.0.14/24
- 192.168.1.14/24
routes:
- to: 0.0.0.0/0
via: 192.168.0.1
metric: 50
- to: 0.0.0.0/0
via: 192.168.1.1
metric: 100
nameservers:
addresses:
- 1.1.1.1
- 192.168.0.1
- 192.168.1.1
Software
This section details software to install for development purposes.
Visual Studio Code
Up until very recently no official Microsoft Visual Studio Code (VSCode) builds exist for ARM. Headmelted provides unofficial builds of VSCode for ARM.
You will need to trust the packagecloud.io headmelted repository GPG key by running the following:
curl https://packagecloud.io/headmelted/codebuilds/gpgkey | sudo apt-key add -
Then follow the instructions to install VSCode.
Bootnote: Microsoft has started to release insider builds of VSCode for both ARMv7 and ARMv8 (64-bit) and an official release is said to be imminent (probably as part of VSCode 1.50).
Docker
Docker installation documentation on Ubuntu.
This is the short version:
sudo apt-get update
sudo apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# Verify that key was loaded
sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
"deb [arch=arm64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
# Verify correct installation of docker:
sudo docker run hello-world
# Add current user for docker administration (remember to restart)
sudo usermod -aG docker $USER
Do a reboot and then check that docker is up and running:
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Python 3
Python 3(.8) is already installed per default.
To install pip (python package manager) and venv (python virtual environment), run:
sudo apt install -y python3-pip python3-venv
Node
The default way of installing Node in Ubuntu is via sudo apt install npm
. However, as of writing the provided version is v10, which is an LTS in Maintenance. v12 is the Active LTS and is therefore preferable.
To install v12 (or later) the Node Version Manager (NVM) can be used. Unlike with apt, NVM and installation of node does not require root privileges and in effect provides a virtual user environment (not unlike pip/python) without the need to install npm packages globally as root.
To install NVM, check latest version (e.g v0.36.0) and then run:
cd
curl https://raw.githubusercontent.com/nvm-sh/nvm/v0.36.0/install.sh -O
chmod ug+x install.sh
./install.sh
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
# Find latest Node v12 version
nvm ls-remote | grep v12 | grep Latest
v12.18.4 (Latest LTS: Erbium)
# Install latest v12 version
nvm install v12.18.4
# Explicitly set the currently active Node version to use
nvm use node v12.18.4
# Validate that the correct version is used
node --version
v12.18.4
Kubernetes
Ubuntu 20.04 comes with a new packaging and deployment tool, Snap. It promises to make software distribution, installation and configuration easer by bundling a given software and its requirements in a single package and to ring-fence the software from other installations on a system.
Opinions on new Snap packaging are mixed but a prime example of Snap in action is the relatively lightweight Snap microk8s Kubernetes distribution, which can be easily installed using: sudo snap install microk8s --classic
.
This also works on the Raspberry Pi 4.
Note that if you do not want microk8s to start by default at every boot, this behaviour can be disabled using sudo snap stop --disable microk8s
.
To start microk8s, run sudo snap start microk8s
. Note that this will take a minute or so to complete at which point the local node should be reporting as ready:
microk8s kubectl get nodes
NAME STATUS ROLES AGE VERSION
rpi4-1 Ready <none> 19d v1.19.0-34+<...>