I’ve recently been able to set up Lemmy and PieFed instances on a Raspberry Pi 5 and wanted to share the process for anyone else interested in self hosting an instance.
The following instructions are based off using a used Raspberry Pi 5 (ARM64) plus a USB external hard drive for the hardware. I used the Raspberry Pi 5 image which is based off Debian 12. The following instructions should be similar enough for other Debian 12 distributions and should hopefully get the same results.
The only other purchase I’ve made was a domain name which was super cheap ($15 a year which includes hiding WHOIS information). Everything else is free.
My residential ISP service blocks incoming data on “business” ports such as Port 80 and 443. Users won’t be able to access your site securely if these ports block incoming data. To work around this I used Cloudflare Tunnels. This allows users to access your site normally. Cloudflare Tunnel will send incoming data to a port of your choosing (between 1024-65,535) and users can access your self-hosted instance.
Cloudflare also has Top Layer Security (TLS) which encrypts traffic and protects connections. This also means your website goes from HTTP:// to HTTPS:// in the address bar. Federation will require TLS so this will be useful. Cloudflare Tunnel also introduces some complications which I’ll address later.
Edited Feb 1/2025
Changed PieFed cron jobs to match recent changes
Adjusted RSync sections
Cloudflared (LOCAL HOST)
!!Only proceed with these instructions after setting Cloudflare as your Primary DNS provider. This process may take up to a day after changing nameservers!!
The following instructions do a few things. First you will install
Cloudflared
(with a ‘d’). Then you will be asked to log in, create a tunnel, run a tunnel and then creating a service (while the current tunnel is running) so your tunnel can run automatically from startup.I’ve noted that this will be installed on the local host (where you are hosting an instance), we will be installing
Cloudflared
on multiple devices for reasons I will cover later. Hopefully this reduces confusion later on.credentials-file: /root/.cloudflared/<Tunnel-UUID>.json
->credentials-file: /home/USERNAME/.cloudflared/<Tunnel-UUID>.json
CTRL + C
to exit this tunneltunnel: TUNNEL_ID credentials-file: /home/USERNAME/.cloudflared/TUNNEL_ID.json ingress: - hostname: DOMAINNAME.COM service: http://localhost:5050 - service: http_status:404
Run as a service
sudo cp ~/.cloudflared/config.yml /etc/cloudflared/config.yml
cloudflared service install
systemctl start cloudflared
systemctl status cloudflared
CTRL + C
when done to exitEnable SSL connections on Cloudflare site
If you used NPM as a reverse proxy and it’s set to port 81, go to any Web Browser and type in your DOMAINNAME.COM. You should be directed to NPM’s login page. Check the address bar and your domain name should have a padlock symbol followed by https://DOMAINNAME.COM. Note that it should read HTTPS:// (with an s) and not HTTP:// (without an s). HTTPS along with the padlock symbol means your connections are properly encrypted.
This is the most complicated step for self-hosting. If you can confirm your connection is encrypt, setting up other services and webapps are fairly straight forward.
Lemmy (LOCAL HOST)
The lemmy instructions are simple and straight forward. When changing the fields asked of you in the instructions, it’s helpful to search and replace the required fields. In
nano
when editing a file, pressCTRL + \
and follow the instructions at the bottom of the window. This will find and replace text.The Lemmy instructions show text for editing with
{{ Example }}
. To avoid confusion, those curly braces must be removed and replaced with the expected data.81
to10633
curl -H 'Accept: application/activity+json' https://DOMAINNAME.COM/u/LEMMY_USERNAME
Updating Lemmy Docker Container
See here for more information.
docker compose down
docker compose pull
docker compose up -d
PieFed (LOCAL HOST)
The PieFed installation instructions will provide more detailed information about each step. This guide does NOT cover any email setup for PieFed.
If you used NPM’s login page to test Cloudflare Tunnels, you will need to login to NPM and change the Port Forward from
81
to8030
PieFed Install Instructions
git clone https://codeberg.org/rimu/pyfedi.git
cd pyfedi
cp env.docker.sample .env.docker
nano .env.docker
nano compose.yaml
8030:5000
. You can change the external container port:8030:
if you are using a custom port. Do NOT touch the internal container port:5000
.ports:
- '8030:5000'
export DOCKER_BUILDKIT=1
sudo docker compose up --build
database system is ready to accept connections
in your terminal window after PieFed is done installing and loading. This means you are ready to attempt a connection through your Web Browser now.cd ~/pyfedi
chown -R USERNAME:USERNAME ./pgdata
sudo docker exec -it piefed_app1 sh
export FLASK_APP=pyfedi.py
flask init-db
exit
CTRL + C
to stop PieFed.docker-compose up -d
USERNAME
to your username.sudo nano /etc/cron.d/piefed
5 2 * * * USERNAME docker exec piefed_app1 bash -c "cd /app && ./daily.sh" 5 4 * * 1 USERNAME docker exec piefed_app1 bash -c "cd /app && ./remove_orphan_files.sh" 1 */6 * * * USERNAME docker exec piefed_app1 bash -c "cd /app && ./email_notifs.sh"
…Continued from PieFed Instructions…
Cloudflare Website Settings
These settings are suggested to help manage traffic. See here for more detailed information.
Create Rule
-> Change the following settings and values on Cloudflare to match what’s listed below:Allow Inbox
/inbox
Create rule
-> Change the following settings on Cloudflare to match what’s listed below:ActivityPub
/activities/
Or
/activities/
/api/
/nodeinfo/
/.well-known/webfinger
+ add setting
Ignore cache-control header and use this TTL
Deploy
to completeCreate rule
againActivityPub2
application/activity+json
Or
application/activity+json
application/ld+json
+ add setting
Ignore cache-control header and use this TTL
10 seconds
Deploy
to complete.env.docker
FileCreate Token
-> ClickGet Started
under Create Custom TokenPieFed
Continue to summary
-> ClickCreate Token
nano ~/pyfedi/.env.docker
CLOUDFLARE_API_TOKEN = 'ZONE.CACHE_PURGE_TOKEN'
CLOUDFLARE_ZONE_ID = 'API_ZONE_ID_TOKEN'
docker compose down && docker compose up -d
Troubleshooting
USERNAME
with your username.cd ~/pyfedi
sudo chown -R USERNAME:USERNAME ./media
Backup/Restore Setup
I decided to keep it simple and use the
rsync
command which comes already installed on Raspberry Pi OS. The guide linked below does a good job of explainingrsync
in a step by step process.Below the linked guide I’ll provide an example of the commands I use to Backup and Restore my raspberry Pi. This creates a copy of the /rootfs folders that make up your Raspberry Pi Operating System and User folders. The commands will exclude some folders that may cause issues when restoring a backup. The guide linked below has more details.
Since I am going to power down the Pi and physically connect it’s hard drive to my computer, I don’t have to worry about making backups on a live and running storage.
The below commands assume I also have an additional EXTERNAL_STORAGE hard drive connected to my computer. This means the backup command will copy the contents from the Raspberry Pi drive (/rootfs folder) to the EXTERNAL_STORAGE drive (/EXTERNAL_STORAGE/backup folder). The restore command will copy the contents from the EXTERNAL_STORAGE drive (/EXTERNAL_STORAGE/backup/rootfs folder) to the Raspberry Pi drive (/rootfs folder)
rsync
WILL delete data on the target location to sync all files and folders from the source location. Be mindful of which direction you are going to avoid any losses. I suggest testing it out on some other folders before commiting to backing up and restoring the entire Raspberry Pi. The guide linked below also covers exclusions to minimize backup sizes.The backup storage MUST be formatted in EXT4 to make sure file permissions and attributes remain the same.
alias rsyncBACKUP="sudo rsync -avxhP --delete --exclude={'proc/','sys/','dev/','tmp/','run/','mnt/','media/','home/USERNAME/.cache','lost+found'} /media/USERNAME/rootfs /media/USERNAME/EXTERNAL_STORAGE/backup/"
rsyncRESTORE="sudo rsync -avxhP --delete --exclude={'proc/','sys/','dev/','tmp/','run/','mnt/','media/','home/USERNAME/.cache','lost+found'} /media/USERNAME/EXTERNAL_STORAGE/backup/rootfs/ /media/USERNAME/rootfs"
. ~/.bashrc
rsBACKUP
rsRESTORE
Firewall (LOCAL HOST)
sudo apt install -y ufw
sudo apt install -y gufw
I haven’t figured out how to properly set this up for myself yet, but I figure it’s probably worth having for an additional layer of protection.
…Continued from PieFed Instructions…
~/pyfedi/.env.docker
file. Look at~/pyfedi/env.sample
and add the other variables to~/pyfedi/.env.docker
according to your needs.nano ~/pyfedi/env.sample
nano ~/pyfedi/.env.docker
docker compose down && docker compose up -d
Updating PieFed Docker Container
docker compose down
git pull
docker compose up --build
docker compose down && docker compose up -d