Small Pubnix

I had a dormant Raspberry Pi 4 that I was gifted but never really used, until I permacomputing-shamed myself into figuring out a use for it. I ended up hosting my own websites on and playing around with gemini and the fediverse. I tried to have some other services running, like a rss client or a read later server, but I ended up to wanting to keep it is a small and low as possible. It looks like this currently

Basic Setup

I used the official Raspberry Pi Imager to burn Alpine to a micro sd card. I have a 32GB card that came with the Raspberry Pi. Next I followed the steps outlined here to have a headless setup, but I didn’t do the memory optimization things yet.

Basically I had to add an archive file to the root of the newly setup sd card, and setup a wireless connection.

$ wget https://github.com/macmpi/alpine-linux-headless-bootstrap/raw/469ee440e7d394cca0976c78f357e7a0e1c82cc4/headless.apkovl.tar.gz
$ cat > wpa_supplicant.conf << EOF
country=FR
network={
    key_mgmt=WPA-PSK
    ssid="mySSID"
    psk="myPassPhrase"
}
EOF

Next:

ssh root@local.ip.address
setup-alpine
reboot

Applications

setup-apkrepos -c
apk add caddy radicale nano

For the moment I ommit information on how to setup the domain, the dns records, the port forwarding, but that’s all part of the setup as well if you want to access the locally running Pi from the outside. I also chose to the applications running under /srv/* together with their data and configs. That makes it easier for me to create propoer backups later, I hope (but I have to set that up yet).

Caddy

Caddy is a web server that can do a lot of things, like serving websites or route incoming requests to the right application. I use it to serve static websites and make calendars and contacts be accessed via subdomain.

After installation I only had to change the config file so I can have my setup in /srv/caddy/Caddyfile.

: ${caddy_opts:="--config /srv/caddy/Caddyfile --adapter caddyfile"}

Radicale

Radicale is a caldav and carddav server. It’s made for hosting calendars, todo lists and contacts. Installing it via Alpine’s package manager does most of the magic, but I adjusted the path so everything was in /srv/radicale.

command_args="--config /srv/radicale/config"
[…]
checkpath -f -o root:radicale -m640 /srv/radicale/config
checkpath -f -o root:radicale -m640 /srv/radicale/rights
checkpath -f -o root:radicale -m640 /srv/radicale/users

Agate

Agate is a very simple gemini server, but it does a fantastic job. The gemtext files live under /srv/agate/data, and I installed the whole thing like this.

apk add gcompat
cd ~
wget https://github.com/mbrubeck/agate/releases/download/v3.3.20/agate.aarch64-unknown-linux-gnu.gz
gunzip agate.aarch64-unknown-linux-gnu.gz
mv agate.aarch64-unknown-linux-gnu /usr/sbin/agate
chmod +x /usr/sbin/agate

The gcompat package is needed because otherwise the binary wouldn’t run. Since agate wasn’t installed via Alpine package manager, I had to add some configs manually. There needs to be a service entry, which we can add to the default service startup as well

mkdir -p /srv/agate/data
touch /etc/init.d/agate
chmod +x /etc/init.d/agate
nano /etc/init.d/agate

My openrc config for agate looks like this.

#!/sbin/openrc-run
name="agate"
description="agate gemini server"
directory="/srv/agate"
command="/usr/sbin/agate"
command_args="--content data/ --addr [::]:1965 --addr 0.0.0.0:1965 --hostname gems.grandmarais.ch --lang en-US"
command_background=true
pidfile="/run/agate.pid"

Now we can add the service and start it.

rc-update add agate default
rc-service agate start

snac

snac2 is an awesome ActivityPub server, and quite easy to install. Grabbed it from the Alpine repository, created the folders and initiated snac, then changed who owns the data folder, so snac can write to it.

apk add snac
mkdir -p /srv/snac
cd /srv/snac
snac init data
chown snac:snac -R data/

Don’t forget to setup fediverse users. snac already has an init.d entry, and I just changed the data folder.

rc-update add snac default
nano /etc/init.d/snac

[...]
: ${SNAC_DATA:="/srv/snac/data"}
[...]

Start said services and/or reboot.

rc-service snac start

Watchdog

Watchdog regularly controls if the Raspberry Pi is still running and reboots if necessary. We have to install it via the edge branch of Alpine.

apk add watchdog --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing/
WATCHDOG_DEV=/dev/watchdog
# WATCHDOG_OPTS="-t 10 -T 20"

Offpunk

Offpunk is a really gread terminal browser that can do http, gemini and gopher, as well as subscribe to websites and feeds and has some other luxurious features. Great thing to have around for all things regarding accessing the web.

apk add offpunk --repository=https://dl-cdn.alpinelinux.org/alpine/edge/testing
apk add py3-beautifulsoup4 py3-readability-lxml

Backups

Is done via webdav in my case. Added the following script to the folder /etc/periodic/daily/ and make it executeable. Also had to apk add curl (install curl).

#!/bin/sh

WEBDAV_URL=""
USERNAME=""
PASSWORD=""

APPS="caddy agate radicale snac"

for APP in $APPS; do
    SOURCE_DIR="/srv/$APP"
    TIMESTAMP=$(date +%Y%m%d_%H%M%S)
    BACKUP_FILE="$APP-$TIMESTAMP.tar.gz"

    echo "Backing up: $SOURCE_DIR"

    # Ensure source dir exists
    if [ ! -d "$SOURCE_DIR" ]; then
        echo "Source directory $SOURCE_DIR does not exist. Skipping."
        continue
    fi

    # Create archive
    tar -czf "$BACKUP_FILE" -C "$SOURCE_DIR" .
    if [ $? -ne 0 ]; then
        echo "Failed to create archive for $APP"
        continue
    fi

    # Upload via WebDAV
    echo "Uploading to: $WEBDAV_URL$BACKUP_FILE"
    curl -v -u "$USERNAME:$PASSWORD" -T "$BACKUP_FILE" "$WEBDAV_URL$BACKUP_FILE"

    # Check upload result
    if [ $? -eq 0 ]; then
        echo "Success: $BACKUP_FILE uploaded"
        rm "$BACKUP_FILE"
    else
        echo "Upload failed for $BACKUP_FILE"
    fi
    echo "---"
done