Install Ubuntu on a Raspberry Pi headless
The download and installation instructions will tell you that you need an HDMI cable or a serial cable. With a bit of preparation, that is actually not necessary.
I am assuming that you have a Linux machine with a Micro SD card reader/writer available. In my case, this also runs Ubuntu. I am also assuming you want to connect your Raspberry Pi to your wired network.
Download Ubuntu
Get the image you want. The link to the download page is above. I have tried this with the 32 bit image of Ubuntu 20.04 LTS "Focal Fossa", trying to revive an old Raspberry PI 2B I had lying around.
Prepare the image
Uncompress the image to a work directory. Put your Micro SD card into
the reader. Find out how big your SD card is (
dmesg
is your friend,
or use
lsblk -b
). In your work directory, create a file that is
the size of your card, like in this example (I already knew that the
card would be
/dev/sdg
, your mileage may vary):
% lsblk -b /dev/sdg
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sdg 8:96 1 15931539456 0 disk
% fallocate -l 15931539456 my_sd_card_image
Now overwrite the beginning of the file with the uncompressed image you downloaded. The special form of output redirection prevents the truncation of the file.
% cat diskimage 1<>my_sd_card_image
Next, create a loop device so you can work with the image. The
-f
parameter looks for the first free loop device, and the
-P
parameter
forces the kernel to read the partition table and create the
appropriate devices. You will need to do a lot of things as root now,
so you can get a root shell at this point.
% sudo -i
# losetup -fP --show my_sd_card_image
/dev/loop3
# ls /dev/loop3*
/dev/loop3 /dev/loop3p1 /dev/loop3p2
First, make the data partition bigger so that it spans the whole image.
Currently, most of the image is actually not allocated. GPartEd is my
preferred tool for this. Start it on the loop device for the whole
image, and re-size the second partition to its maximum size. You can
use
fdisk
and
resize2fs
if you want; it isn't difficult either.
# mount /dev/loop3p2 /mnt
Now let's edit some files. Make sure you don't edit the files on your
machine under
/etc
but the files on the image under
/mnt/etc
!
Add a user for yourself. You do this by adding a line to
/mnt/etc/passwd
:
martini:x:1001:1001:Martin Ibert:/home/martini:/bin/bash
And this to
/mnt/etc/shadow
. The number
18192
should be replaced
by the days since the epoch (
expr $(date +%s) / 86400
).
martini:*:18192:0:99999:7:::
Now edit
/mnt/etc/group
and add yourself to the following groups:
adm
sudo
. Also add the following line:
martini:x:1001:
Edit
/mnt/etc/sudoers
so that the middle section reads as follows.
The file is read-only, so you need to write it out with
:x!
.
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
# Members of the adm group may gain root privileges
%adm ALL=(ALL) NOPASSWD: ALL
Finally, give yourself a rudimentary home directory (obviously, use the
SSH key you want, or use your regular
authorized_keys
file):
# mkdir -p /mnt/home/martini/.ssh
# cp ~martini/.ssh/id_rsa_np.pub /mnt/home/martini/.ssh/authorized_keys
# chmod 700 /mnt/home/martini/.ssh
# chmod 600 /mnt/home/martini/.ssh/authorized_keys
# chown -R 1001:1001 /mnt/home/martini
Unmount the image.
# umount /mnt
Write the image to the card
If you write the image with
dd
, it will take forever for a big Micro
SD card. So we will be doing it differently.
First, we'll put the partition table on the card. That is in the first megabyte of the image:
# dd if=/dev/loop3 of=/dev/sdg count=2048
# ls /dev/sdg*
/dev/sdg /dev/sdg1 /dev/sdg2
You now need
partclone
. Install it if you don't have it yet. We use
it to quickly put just the data that means something onto the card,
rather than dump a ton of blocks that have no useful data in them.
(Output not shown.) The boot partition can be dumped normally as it is
not very big. Make sure it is actually the size shown.
# dd if=/dev/loop3p1 of=/dev/sdg1 bs=256M
# sync
# partclone.ext4 -b -s /dev/loop3p2 -o /dev/sdg2
Don't panic if it seems to hang after saying
Syncing...
. If your
machine has ample RAM, this is when the actual writing to the card will
take place. Everything before that happens in memory if there is
enough of it.
Put the card in the Raspberry PI and boot it
In goes the card, and then connect your Raspberry PI to your LAN and
power it up. Watch your DHCP server to find out its address (in my
case, I knew the MAC beforehand and could configure DHCP ahead of
time, but if you don't, you need to find the latest device to get an
address). It still takes a few seconds for the SSH server to come up
after the address is assigned. All my Respberry Pis have MAC addresses
beginning with
b8:27:eb
. The internet says that
dc:a6:32
is also a
possibility.
When you power up the Raspberry Pi, pay attention to the yellow LED. It should light up briefly. After that, nothing appears to be happening for a while. Be patient.
You should now be able to
ssh
into your Raspberry Pi and
sudo
from
there to complete the configuration of the system.
Don't forget to destroy the loop device.
# losetup -d /dev/loop3