This guide shows how to perform a Headless Installation of Alpine Linux 3.14 64bit on a Raspberry Pi 4 B with Persistent Storage, using a Linux Workstation.
The term Headless Installation means that the Raspberry Pi will be only accessed via Network (ssh
) since the first boot, and Persistent Storage means that will be set up a second partition to persistently hold the user files and the data generated by the services the Raspberry Pi may serve.
The system will be installed as Diskless Mode and any changes to the system will be saved using the Alpine Local Backup.
You don’t need to be a experienced Linux user to follow this guide! It is just required that you are a curious person with basic command-line interface skills such as, to execute commands with sudo
privileges and editing text files.
Requirements:
A 15.3W USB-C power supply (official or similar).
A Class 10 MicroSD card of 16GB or more.
A computer running Linux and
sudo
privileges.Cable or WiFi internet connection allowing the Raspberry Pi and the Linux computer to be connected to the same network.
Create a Bootable MicroSD Card With Two Partitions
The goal is to have a MicroSD card containing two partitions:
The system partition: A fat32
partition, with boot
and lba
flags, on a small part of the MicroSD card, enough to store the system and the applications (suggested 256MB to 2GB).
The storage partition: A ext4
partition occupying the rest of the MicroSD card capacity, to use as persistent storage for the users and for the data generated by the services the Raspberry Pi may serve.
Identify the MicroSD card
Do not plug it into your computer and run the command:
lsblk
Take note of the output, plug the MicroSD card into your computer and run
lsblk
again. Sample output:NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 223,6G 0 disk ├─sda1 8:1 0 600M 0 part /boot/efi ├─sda2 8:2 0 1G 0 part /boot └─sda3 8:3 0 222G 0 part /home sdb 8:16 0 465,8G 0 disk └─sdb1 8:17 0 465,8G 0 part sdc 8:32 0 465,8G 0 disk └─sdc1 8:33 0 465,8G 0 part sdd 8:48 1 59,5G 0 disk └─sdd1 8:49 1 59,5G 0 part /run/media/rossijonas/67E7-60D4 zram0 252:0 0 4G 0 disk [SWAP]
Notice that
sdd
is the 64gb MicroSD card (in this case), and it has one partionsdd1
mounted.From now on, you may copy/paste the commands, but remember to replace
sdd
with the MicroSD disk name identified on your machine.
Cleanup
Unmount the partition(s):
sudo umount /dev/sdd1
or
sudo umount /dev/sdd?*
(If the device has more than one partition mounted.)Remove the partition(s):
sudo parted /dev/sdd rm 1
(Repeat that for other existent partitions if needed, replacing
1
with the partition number.)Erase filesystem/partition-table signatures:
sudo wipefs -af /dev/sdd
Verify that the device has no label applied and an empty partition table:
sudo parted /dev/sdd print
Sample output:
Error: /dev/sdd: unrecognised disk label Model: Generic STORAGE DEVICE (scsi) Disk /dev/sdd: 63,9GB Sector size (logical/physical): 512B/512B Partition Table: unknown Disk Flags:
Create the Desired Partitions
Create MBR partitioning scheme on the MicroSD card:
sudo parted /dev/sdd mklabel msdos
(You may run
sudo parted /dev/sdd print
anytime to observe the changes applied.)Create the system partition (2GB):
sudo parted /dev/sdd mkpart primary 1 2g
Add
boot
andlba
flags to the system’s partition:sudo parted /dev/sdd set 1 boot on
sudo parted /dev/sdd set 1 lba on
Create the storage partition (62GB):
sudo parted /dev/sdd mkpart primary 2001 64g
(Replace
64g
with the size of your MicroSD card, ex.:16g
.)Build a
fat32
filesystem on partition1
and anext4
filesystem on partition2
:sudo mkfs.vfat -F 32 /dev/sdd1
sudo mkfs.ext4 /dev/sdd2
Verify the MicroSD card’s state with
sudo parted /dev/sdd print
again, it should end up with a scheme like this:Model: Generic STORAGE DEVICE (scsi) Disk /dev/sdd: 63,9GB Sector size (logical/physical): 512B/512B Partition Table: msdos Disk Flags: Number Start End Size Type File system Flags 1 1049kB 2000MB 1999MB primary fat32 boot, lba 2 2001MB 63,9GB 61,9GB primary ext4
Flash Alpine Linux Into the MicroSD Card
Download Alpine Linux for Raspberry Pi (
aarch64
for Raspberry Pi 4 B).Eject and remove the MicroSD, and then plug it on again.
Run
lsblk
to verify both partition mount points.Sample output:
sdd 8:48 1 59,5G 0 disk ├─sdd1 8:49 1 476M 0 part /run/media/rossijonas/4F85-22E7 └─sdd2 8:50 1 59,1G 0 part /run/media/rossijonas/6a9f9b14-628b-431f-9f3c-721d526
Extract the tarball into the system’s (bootable
fat32
2GB) partition:cd /run/media/rossijonas/4F85-22E7
tar -zxvf ~/Downloads/alpine-rpi-3.14.0-aarch64.tar.gz
(Substitute directories and filenames.)
Since it will be a headless install (without an external monitor plugged in) you can setup minimum memory usage for the GPU, maximizing available memory, via a user custom configuration file:
(Make sure you’re still on the same working directory as the previous command. Check with
pwd
.)echo "gpu_mem=32" > usercfg.txt
Set Up for the Headless Installation
To perform a Headless Installation it is required to have an overlay file on the root of the bootable partition, containing some predefined networking configuration that will allow the Raspberry Pi to connect and to be accessible on the local network.
Verify the Local Network Settings, and Identify an Available IP Number
Install
nmap
from the package manager:RHEL/CentOS/Fedora/RockyLinux -
sudo dnf install nmap
Ubuntu/Mint/Kali/Debian(or other Debian-based) -
sudo apt install nmap
…or from any of the alternative ways.
Verify the IP address of your computer on the subnet it is connected:
hostname -I
Sample output:
192.168.0.129 192.168.122.1 10.19.0.12 100.85.0.1 fdeb:446c:912d:8da:: fdeb:446c:912d:8da::
What you need is the first IP from the output
192.168.0.129
(on this example), and from that, assuming the computer is connected with standard configuration, the gateway IP address will be192.168.0.1
.Scan the local subnet, to identify IPs currently in use:
nmap -sn 192.168.0.0/24
Sample output:
Starting Nmap 7.80 ( https://nmap.org ) at 2021-06-22 10:01 -03 Nmap scan report for _gateway (192.168.0.1) Host is up (0.00036s latency). Nmap scan report for 192.168.0.128 Host is up (0.0053s latency). Nmap scan report for fd2lm (192.168.0.129) Host is up (0.000038s latency). Nmap scan report for 192.168.0.145 Host is up (0.0053s latency). Nmap done: 256 IP addresses (4 hosts up) scanned in 3.26 seconds
Verify what IPs are in use and must not be reassigned. On the example, it is the IPs ending in
.128
,.129
, and.145
.
Configure, Build and Add the Overlay File Into the Bootable Partition
It could be a bit tricky to manually set up the directories/files/scripts needed to build the overlay file from scratch (and it could be another blog post) so, to simplify, we are going to use a ‘boilerplate’ solution and assign the required configuration on top of it.
Download this repository https://github.com/mesca/alpine_headless.
(This is a crafted solution to simplify the creation of an overlay file that temporarily sets up the network connection and the SSH server for the Alpine’s first boot on the Raspberry Pi.)
Apply the network configurations:
With the repository on your machine, edit the following file on your code editor:
alpine_headless/ovl/etc/network/interfaces
Replace the
gateway
address with your subnet gateway address (192.168.0.1
in this example), and assign a static IP address by replacing theaddress
with an available IP address as previously verified. I will use the ending.125
for this example.The file will look similar to this:
auto lo iface lo inet loopback auto eth0 #iface eth0 inet dhcp iface eth0 inet static address 192.168.0.125 netmask 255.255.255.0 gateway 192.168.0.1 auto wlan0 iface wlan0 inet dhcp
Setup the WiFi connection:
(You may skip this step if the Raspberry Pi will connect via network cable.)
Edit the following file on your code editor:
alpine_headless/ovl/etc/wpa_supplicant/wpa_supplicant.conf
Change the local WiFi network name (
ssid
) and the password (psk
), example:network={ ssid="change_me" psk="change_me" }
Build the overlay file:
Make sure you are at the repository’s
alpine_headless/
folder, and run:./make.sh
Copy the overlay file to the root of the MicroSD boot partition:
cp localhost.apkovl.tar.gz /run/media/rossijonas/4F85-22E7
(Replace the MicroSD mount point. Check again with ‘lsblk’ if needed.)
Double check the MicroSD boot partition, it must contain the Alpine Linux files, the
usercfg.txt
file and thelocalhost.apkovl.tar.gz
file:ls -la /run/media/rossijonas/4F85-22E7
Output should look similar to this:
total 5548 drwxr-xr-x. 5 rossijonas rossijonas 4096 dez 31 1969 . drwxr-x---+ 4 root root 80 jun 22 11:09 .. drwxr-xr-x. 3 rossijonas rossijonas 4096 jun 15 11:35 apks drwxr-xr-x. 2 rossijonas rossijonas 4096 jun 15 11:35 boot drwxr-xr-x. 2 rossijonas rossijonas 20480 jun 15 11:35 overlays -rw-r--r--. 1 rossijonas rossijonas 25 jun 15 11:35 .alpine-release -rw-r--r--. 1 rossijonas rossijonas 26894 jun 11 05:10 bcm2710-rpi-2-b.dtb -rw-r--r--. 1 rossijonas rossijonas 28392 jun 11 05:10 bcm2710-rpi-3-b.dtb -rw-r--r--. 1 rossijonas rossijonas 29011 jun 11 05:10 bcm2710-rpi-3-b-plus.dtb -rw-r--r--. 1 rossijonas rossijonas 26890 jun 11 05:10 bcm2710-rpi-cm3.dtb -rw-r--r--. 1 rossijonas rossijonas 49214 jun 11 05:10 bcm2711-rpi-400.dtb -rw-r--r--. 1 rossijonas rossijonas 49218 jun 11 05:10 bcm2711-rpi-4-b.dtb -rw-r--r--. 1 rossijonas rossijonas 49892 jun 11 05:10 bcm2711-rpi-cm4.dtb -rw-r--r--. 1 rossijonas rossijonas 20120 jun 11 05:10 bcm2837-rpi-3-a-plus.dtb -rw-r--r--. 1 rossijonas rossijonas 20525 jun 11 05:10 bcm2837-rpi-3-b.dtb -rw-r--r--. 1 rossijonas rossijonas 20989 jun 11 05:10 bcm2837-rpi-3-b-plus.dtb -rw-r--r--. 1 rossijonas rossijonas 19852 jun 11 05:10 bcm2837-rpi-cm3-io3.dtb -rw-r--r--. 1 rossijonas rossijonas 52456 jun 15 06:06 bootcode.bin -rw-r--r--. 1 rossijonas rossijonas 60 jun 15 11:34 cmdline.txt -rw-r--r--. 1 rossijonas rossijonas 408 jun 15 11:34 config.txt -rw-r--r--. 1 rossijonas rossijonas 5448 jun 15 06:06 fixup4.dat -rw-r--r--. 1 rossijonas rossijonas 7314 jun 15 06:06 fixup.dat -rw-r--r--. 1 rossijonas rossijonas 927 jun 22 11:10 localhost.apkovl.tar.gz -rw-r--r--. 1 rossijonas rossijonas 2229120 jun 15 06:06 start4.elf -rw-r--r--. 1 rossijonas rossijonas 2953312 jun 15 06:06 start.elf -rw-r--r--. 1 rossijonas rossijonas 11 jun 21 12:11 usercfg.txt
Eject and remove the MicroSD card, it’s finally ready for the first boot!
Headless Installation, Setup & Persistent Storage
Remote Access to the Raspberry Pi
Insert the MicroSD card into the Raspberry Pi, plug the network cable (if needed), and power on.
After a few seconds, run
nmap -sn 192.168.0.0/24
again and verify that the Raspberry Pi is connected with the IP address previously defined.Remotely access the Raspberry Pi via SSH as
root
user with no password:ssh root@192.168.0.125
(Remember to replace the IP address from this example.)
If you get an error like this:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! (...)
That means that there was already another system using the static IP assigned for the Raspberry Pi, and there is a fingerprint on your local machine associated with this remote.
If that error happens, edit the file
~/.ssh/known_hosts
removing the line that starts with the IP address assigned to the Raspberry Pi.If the SSH access was successful, you will see a prompt like this:
Welcome to Alpine! The Alpine Wiki contains a large amount of how-to guides and general information about administrating Alpine systems. See <http://wiki.alpinelinux.org/>. You can setup the system with the command: setup-alpine You may change this message by editing /etc/motd. (none):~#
(Notice that the prompt line ending with
#
shows that you are logged asroot
user.)
Alpine Linux Initial Setup
Perform Alpine’s initial setup:
setup-alpine
You will be prompted to assign/select:
- Keyboard layout
- Hostname (You may leave empty, use default)
- Network Interface (You may leave empty, use default)
- IP Address (You may leave empty to keep using current IP address assigned)
- Netmask (You may leave empty, use default)
- Gateway (You may leave empty, use default)
- Network Manual Configuration (You may leave empty, use default)
- Gateway (You may leave empty, use default)
- DNS Domain Name (You may leave empty, use default)
- DNS Domain Server (You may leave empty, use default)
- Change Root Password (You must create the
root
password) - Timezone
- HTTP/FTP Proxy (You may leave empty)
- NTP client (You may leave empty, use default)
- Repository Mirror (You may leave empty, use default)
- SSH Server (You may leave empty, use default)
- “Enter where to store configurations” (Assign
mmcblk0p1
if not selected by default.) - Cache Dir (Assign
/media/mmcblk0p1/cache
if not selected by default.)
Update the system after setup:
apk update && apk upgrade
Save all the changes applied by
setup-alpine
and the system update:By default, Alpine Linux is installed as Diskless Mode, and any changes might be saved using Alpine Local Backup (lbu).
lbu commit -d
Add basic configuration to the new SSH server installed:
The overlay file created previously won’t run anymore since the system was installed and set up via the
setup-alpine
script. That means the SSH server we accessed on the first boot will not be set anymore.Before the first reboot, the SSH server installed by the
setup-alpine
script must be configured to allow remote access again.Open the following file on the text editor of your choice (
vi
/nano
):(Alpine Linux uses
vi
as the default text editor, If you are not comfortable withvi
/vim
, I recommend you to installnano
to perform this task:apk add nano
.)vi /etc/ssh/ssd_config
ornano /etc/ssh/sshd_config
Find the following directives, uncomment and assign the values like the example below, then save and quit the editor:
(...) Port 22 (...) PermitRootLogin yes (...) PubkeyAuthentication yes (...) PasswordAuthentication yes PermitEmptyPasswords no (...)
Commit the changes and reboot:
lbu commit -d
reboot
After reboot, access the Raspberry Pi as
root
again:ssh root@192.168.0.125
. This time you will be prompted for the root password created.
Set Up the Persistent Storage
Set up the second MicroSD partition to be used as the home directory for the user that will be created and also to serve as persistent storage.
Verify the presence of both partitions with the command:
blkid
Sample output:
/dev/mmcblk0p2: UUID="8a98a5df-e8a9-4f03-b1f6-6c9ecad2e854" TYPE="ext4" /dev/mmcblk0p1: UUID="0815-9310" TYPE="vfat"
Create a mount point for the second partition (
mmcblk0p2
):mkdir /media/mmcblk0p2
Add the mount instruction to
fstab
(and mount all again):
echo "/dev/mmcblk0p2 /media/mmcblk0p2 ext4 rw,relatime 0 0" >> /etc/fstab
mount -a
(You may double-check that it is mounted with mount
.)
Commit the changes:
lbu commit -d
Create an Admin User
Create the user’s (
user1
) home directory on the persistent storage:mkdir -p /media/mmcblk0p2/home/user1
Create the admin user (
user1
):adduser -h /media/mmcblk0p2/home/user1 user1
Add user to
wheel
group:adduser user1 wheel
Install
sudo
:apk add sudo
Allow users from
wheel
group full access to the system, by applyingsudo
command and providing the user password:visudo
This command will open the
/etc/sudoers
file on thevi
text editor. You must find and uncomment the following line:# %wheel ALL=(ALL) ALL
Instructions for who never used the
vi
/vim
editor:- Navigate down to find the line we want, by using the arrow keys.
- When the line is found, place the cursor on top of the
#
character and typex
key. - Type
:x
and press Enter to save and leave.
Commit the changes again:
lbu commit -d
Update the SSH server configuration to block
root
login attempts:(From now on the system will be accessed remotely by the admin user created
user1
.)Open the following file on the text editor of your choice (
vi
/nano
):vi /etc/ssh/ssd_config
ornano /etc/ssh/sshd_config
Find the following directive, assign the value, save and quit the editor:
(...) PermitRootLogin no (...)
Commit and reboot:
lbu commit -d
reboot
After reboot, you are able to connect via SSH as the admin user created:
ssh user1@192.168.0.125
Issue the command
whoami
to verify the current user name and the commandpwd
to verify the home directory.Sample Output:
Welcome to Alpine! The Alpine Wiki contains a large amount of how-to guides and general information about administrating Alpine systems. See <http://wiki.alpinelinux.org/>. You can setup the system with the command: setup-alpine You may change this message by editing /etc/motd. rpi4b-s1:/$ whoami user1 rpi4b-s1:/$ pwd /media/mmcblk0p2/home/user1
Verify that there’s the
$
sign at the end of the user command prompt, which means you are logged as a regular user.
Post Installation
The Alpine installation is ready and the Raspberry Pi is good to go! In this section, I will recommend some tweaks you may find useful.
Before the tweaks I would like to reinforce the storage strategy in place:
Any changes to the system including configuration and packages installations must be saved manually using Alpine Local Backup (lbu commit -d
), otherwise, it won’t survive a system reboot.
Any user files and user configurations stored under user’s home directory will normally persist a system reboot, as well as any other files stored at the second partition, mounted on /media/mmcblk0p2
.
Recommended Tweaks
Install
man
pages.man
is your best friend and Alpine Linux doesn’t provide it by default:sudo apk add mandoc man-pages mandoc-apropos less less-doc
Enable
edge
repositories to be able to install a variety of packages.Open the
/etc/apk/repositories
on the text editor:sudo vi /etc/apk/repositories
Uncomment the entries for
community
andmain
edge
repositories:/media/mmcblk0p1/apks http://dl-cdn.alpinelinux.org/alpine/v3.14/main #http://dl-cdn.alpinelinux.org/alpine/v3.14/community http://dl-cdn.alpinelinux.org/alpine/edge/main http://dl-cdn.alpinelinux.org/alpine/edge/community #http://dl-cdn.alpinelinux.org/alpine/edge/testing
Save and quit the text editor.
Install
lm-sensors
to quick check the Raspberry Pi CPU temperature in a human-readable form:sudo apk add lm-sensors
Check CPU temperature with the command:
sensors
.Commit the post-installation changes:
lbu commit -d
.Finally, extra step, show off!
- Install
neofetch
:sudo apk add neofetch
. - Run
neofetch
command! It will print the system information on the terminal, just like the header image from this post. - Print or take a picture of the screen.
- Tweet the picture saying: “Thanks @rossijonas”
- Install
Useful links & references:
- Alpine Linux Wiki
- Raspberry Pi Documentation
- “Mastering Vim Quickly” Book (and screencasts) by Jovica Ilic. In this guide you might noticed that
vi
/vim
are very common tools on the Linux ecosystem. If you would like to gain or improve Vim skills very fast, that is the book I recommend. Make sure you subscribe to the newsletter to get free Vim tips and book excerpts on your email.
(👆 This is an affiliate link. If you got any value from this post and are also interested in buying the MVQ book/screencasts, please consider visiting the affiliate link and you’ll be gifting me a coffee ☕ or a beer 🍺.)
Footnotes:
- Follow me on Twitter to get more posts like this and other quick tips in your feed.
- If you have any doubts or tips about this article, I’d appreciate knowing and discussing it via email.
- Do you have any other Linux tips? Would you like to publish that in this blog? Please send an email to all drops.
- As English is not my native language, I apologize for the errors. Corrections are welcome.
- Contact: contact [@] alldrops [.] info.