Kevin's Blog

Raspberry Pi Home Server

Last updated on

I recently soft-bricked my Raspberry Pi docker installation (i.e. I can’t be bothered to keep googling the cryptic error messages), and it was still on Raspbian Stretch which no longer receives support…

So time to reinstall everything on my Raspberry Pi! Might as well document this in case I need to perform disaster recovery.

The audience of this blog is myself in the future.

Hardware

  1. A Raspberry Pi.
    • I have a Raspberry Pi 3 Model B Rev 1.2
  2. SD card
  3. A way to write to SD cards (an SD card slot or adapter)
  4. (optional) External hard drive with a separate power supply

Burning Image to SD Card

Use imager: https://www.raspberrypi.com/software/ to install the latest Raspberry Pi OS for your Raspberry Pi.

Configure password in settings, and enable SSH via username and password, OR go ahead and add an ssh key at this step.

Network Config

Now connect the ethernet (if you haven’t already)

Go to your Router and look for something like “DHCP Reservation”. Assign an IP like 192.168.1.201

Enabling Safe(r) SSH

Do this if you didn’t already set up an ssh key while configuring the image.

First, add a ssh client public key to ~/.ssh/authorized_keys

ssh <username>@192.168.1.201
cd ~/
mkdir .ssh
vi .ssh/authorized_keys

Then, disable password auth

sudo vi /etc/ssh/sshd_config

Set

PasswordAuthentication no

You may also have to check /etc/ssh/sshd_config.d/ for any override files.

Then run

sudo systemctl restart ssh

Install Docker

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
rm get-docker.sh
sudo usermod -a -G docker <username>
sudo systemctl enable docker

I do not recommend rootless anymore, it’s easier when moving docker files into another partition.

Then relogin in. Confirm success with a docker ps and docker run hello-world

If that fails due to connect: network is unreachable

Detour: Fix resolvconf

sudo apt install resolvconf
sudo vi /etc/resolvconf/resolv.conf.d/original

Remove this line:

nameserver 192.168.1.1
sudo vi /etc/resolvconf/resolv.conf.d/base

Add these lines:

nameserver 8.8.8.8
nameserver 8.8.4.4

Run

sudo resolvconf -u

Detour of Detour: Fix personal router DNS

Servers allocated with DHCP requests:
DHCP DNS Type: Default Servers Custom Servers
Primary DNS:
Secondary DNS:

Then sudo reboot now

Disable desktop on boot

raspi-config can do this, just boot to command line instead of GUI.

Now that there are no “lite” images published, this has to be done.

External Hard Drive

Assuming external hard drive is located at /dev/sda2

sudo mkdir -p /path/to/hard/drive
sudo mount /dev/sda2 /path/to/hard/drive -o uid=<username>,gid=<username>

To mount on startup, first find the uuid of the hard drive

lsblk -f

Then edit the file system table, /etc/fstab, and add:

UUID=<UUID> /path/to/hard/drive ntfs defaults,noatime,nofail 0 2

Replace ntfs with the correct filesystem. Be careful! This could brick your boot if incorrectly.

Docker containers/images on External Hard Drive

You may want to do this in combination with the next step (OverlayFS to reduce writes) so docker images are stored on the hard drive, not the SD card.

First, a place for docker files:

mkdir -p /mnt/path/to/docker
sudo nano /etc/docker/daemon.json
{
  "data-root": "/mnt/path/to/docker"
}

Additionally, after Docker 29, also a place for containerd images:

sudo nano /etc/containerd/config.toml

Add root = "/var/lib/containerd"

Finally, require mounting your hard drive partition before starting Docker:

sudo systemctl edit docker
[Unit]
RequiresMountsFor=/mnt/path/to/docker

OverlayFS to reduce writes

This step will make every other operation more of a pain in the ass due to the write protection, so do this last.

Generally followed https://www.and-e.co.uk/sysadmin/raspberryPiOverlayFileSystem.html

raspi-config

Answer yes to both “Would you like the overlay file system to be enabled?” and “Would you like the boot partition to be write-protected?” but do NOT reboot yet.

If you have an external hard drive, you’ll want to first make sure external hard drives are not write protected.

sudo mount -o remount,rw /boot/firmware
sudoedit /boot/firmware/cmdlint.txt from

Change overlayroot=tmpfs to overlayroot=tmpfs:recurse=0

Then undo the temporary mount

sudo mount -o remount,ro /boot/firmware

And then you can reboot.

Docker Containers

Remember to use image for the right architecture for your Raspberry Pi model. For my model, it’s arm64v81.

linuxserver.io has a lot of community maintained images for popular server software.

I keep all my docker-compose files on the hard drive, which makes the docker images below even easier to recover when the SD card corrupts (which it does often!)

Set PUID and GUID to your non-root user, probably 1000, but you can check using

id -u <username>

restart: unless-stopped allows these containers to be restarted on host startup

ddclient

---
version: "2"
services:
  ddclient:
    image: linuxserver/ddclient:arm64v8-latest
    container_name: ddclient
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=Europe/London
    volumes:
      - /path/to/hard/drive/ddclient/config:/config
    restart: unless-stopped

Transmission

---
version: "2"
services:
  transmission:
    image: linuxserver/transmission:arm64v8-2.94-r1-ls14
    container_name: transmission
    environment:
      - PUID=1000
      - PGID=1000
      - TZ=America/Los_Angeles
    volumes:
      - /path/to/hard/drive/transmission/config:/config
      - /path/to/hard/drive/transmission/downloads:/downloads
      - /path/to/hard/drive/transmission/watch:/watch
    ports:
      - 9091:9091
      - 51413:51413
      - 51413:51413/udp
    restart: unless-stopped

Plex

---
version: "2"
services:
  plex:
    image: linuxserver/plex:arm64v8-1.32.2
    container_name: plex
    network_mode: host
    environment:
      - PUID=1000
      - GUID=1000
      - TZ=America/Chicago
    volumes:
      - /path/to/hard/drive/media:/mnt/media
    restart: unless-stopped

Operating

sudo docker compose up -d

Not using sudo will cause issues.

Containers should start back up on restart, too.

This guide has been used 2 times by myself and saved my ass.

Footnotes

  1. In the first version of this post I went through these instructions for arm32v7, only to learn at the end that support for it is getting removed at the beginning of July.. So I went through all of these steps again for arm64v8. This also meant this blog was immediately useful to me less than 24 hours later :)