Installing Lemmy & PieFed using Docker on a Raspberry Pi 5 using Cloudflare Tunnel
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.
Set Cloudflare as your Domain Name's primary Domain Name Servers (DNS). See here
Do this up to a day before in advance as changes may take up to a day to take effect.
Raspberry Pi 5 with Raspberry Pi OS (64) image installed
You can use other hardware with Debian 12 Operating Systems but I can't guarantee these instructions will be the exact same
A USB external hard drive
Something with good read/write speeds will help
Access to any routers on your private network
You will need access to Port Forwarding options. You will need to read any router manuals or ask your Internet Service Provider since this is different for every device.
The required software to host Lemmy or PieFed will include
Docker
Cloudflared
Lemmy or PieFed
Additional software I will also cover but aren't necessary are:
Nginx Proxy Manager (NPM)
UFW/GUFW - Simple Firewall
RSync - For making backups
Termux - Android terminal app that will be used for remote SSH access
Docker (LOCAL HOST)
The official Docker instructions are clear, quick and simple. The process will also add their repository information for quick and easy updates. This will be installed as a service on your operating system.
Pick a port number between 1024-65,535. This is how Cloudflare will send data and remote connections to your instance without worrying about blocked ports. I like to use 5050 because it's simple, easy to remember and not used by any of my other self-hosted services. To be consistent, for the rest of this guide I will use port 5050 as an example. Feel free to replace it with any port number you feel like using.
Router settings are different for each device, refer to a manual or call your ISP for support depending on your situation.
SSH login to your Raspberry Pi and enter the command hostname -I
This will print the IP addresses used by the host machine. The first IP address printed will be your local IP address. The rest of the addresses are NOT needed and can be ignored.
Access your Port Forwarding settings in your private network router.
Find your Raspberry Pi device by the Local IP address
Add a rule to allow TCP connections on port 5050.
If your router port forwarding settings show Internal and External fields, simply add 5050 to both fields.
Save
If you are only hosting a Lemmy or PieFed instance, you will be able to do that without the need of a Reverse Proxy which is described below. In this case you can simply use the default ports for Lemmy or PieFed. Replace my example port 5050 with the following depending on your needs:
Lemmy default port: 10633
PieFed default port: 8030
Reverse Proxy
A reverse proxy allows the local host machine to distribute incoming user connections to different services hosted on the local machine. For example, all data from Cloudflare comes in on port 5050 when accessing the DOMAINNAME.COM address. I can use Subdomains to redirect incoming connections on port 5050 to open ports on my local host machine.
For example, both Lemmy and PieFed can be hosted at the same time. We can use the subdomains lemmy. and piefed. to redirect traffic. When a user types lemmy.DOMAINNAME.COM into the address bar, Cloudflare will send the connection through 5050 to your home and private router which then continues to the Reverse Proxy. The Reverse Proxy running on the local host machine will catch the subdomain request and immediately switch to port 10633 where a connection to Lemmy will be completed. Typing in piefed.DOMAINNAME.COM will guide all requests to port 8030 where PieFed is running and complete that connection.
For simplicity, Nginx Proxy Manager is docker based with an easy to use web user interface that's accessible through your local network connected Web Browser. It has it's limitations but works fine for the current needs.
Nginx Proxy Manager (LOCAL HOST)
NPM is extremely simple to set up. Simply create a new folder, create a docker-compose.yml file filled with the necessary information and then run the container.
Note that port 5050: externally connects to NPM internally through port :80. Make sure 5050 matches the Cloudflare Tunnel port you have decided on using.
docker compose up -d and wait for the services to start running
In your Web Browser on any device connected to your private network, type your Raspberry Pi's local address followed by :81 into the address bar. For example 192.168.0.100:81. See Port Fowarding for help finding your local IP address.
The login page will ask for account details. Enter the following:
Press Enter to store that domain name, NPM won't store your domain name if you don't hit enter
Forward Hostname/IP field: Your local host machine ip address (example 192.168.0.100). See Port Fowarding for help finding your local IP address.
Forward Port field: I like to use port 81 to test Cloudflare Tunnels before installing Lemmy or PieFed. This is the login page for NPM. This can be quickly changed to the ports listed below after confirming a secure connection from Cloudflare Tunnels.
Lemmy: 10633
PieFed: 8030
Block Common Exploits: Enabled
Websockets Support: Enabled
Save
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.
You will get an error if you do not copy your config.yml from your Home folder to /etc/cloudflared. You will need to copy this file again if you make any changes to the config.yml such as adding more tunnels. This will be covered later when setting up Remote SSH access.
Check to see if things are green and working, then press CTRL + C when done to exit
You can now stop the running tunnel from the first step as previously stated (See Step 1.iv.)
You can close this terminal window now
Enable SSL connections on Cloudflare site
Log in to your account on Cloudflare and simply click on the following links
From the main page -> Your DOMAINNAME.COM -> SSL/TLS -> Configure -> Full -> Save
SSL/TLS -> Edge Certificates -> Change the following settings on Cloudflare to match what's listed below:
Always Use HTTPS: On
Opportunistic Encryption: On
Automatic HTTPS Rewrites: On
Universal SSL: Enabled
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, press CTRL + \ 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.
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 to 10633
Click Hosts -> Proxy Hosts -> Click the 3-Dots for your DOMAINNAME.COM proxy rule -> Edit & Save
Note ports 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.
You may see a message that says 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.
If you see constant permission errors, Open and SSH login to the Raspberry Pi in a new terminal window and do the following to allow PieFed to access the required folders:
cd ~/pyfedi
chown -R USERNAME:USERNAME ./pgdata
You can leave this window open, it can be used for the step 5.
You may see an "Internal Server Error" after your first connection attempt. This is normal. You will see movement in your terminal window on each attempt to connect to PieFed. Now you can proceed to initialize the database.
Initialize Database
Open and SSH login to the Raspberry Pi in a new terminal window
sudo docker exec -it piefed_app1 sh
export FLASK_APP=pyfedi.py
flask init-db
Enter username/email/password. Email is optional.
Access PieFed from your Web Browser again. PieFed should now display. You can log in as admin with the same username and password.
exit
You can close this terminal window now
Return to the terminal with the running docker build and press CTRL + C to stop PieFed.
Run PieFed in the background
docker-compose up -d
Setup Cron (Automated) Tasks
This will set up automated tasks for daily maintenance, weekly maintenance and email notifications.
Some functions such as email or captcha's won't work unless you add the necessary variables into the ~/pyfedi/.env.docker file. Look at ~/pyfedi/env.sample and add the other variables to ~/pyfedi/.env.docker according to your needs.
View the sample file
nano ~/pyfedi/env.sample
Edit & Save .env.docker file
nano ~/pyfedi/.env.docker
Restart PieFed Docker container
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
Cloudflare Website Settings
These settings are suggested to help manage traffic. See here for more detailed information.
Exclude Settings
From the main page -> Your DOMAINNAME.COM -> Security -> WAF -> Custom Rules -> Click Create Rule -> Change the following settings and values on Cloudflare to match what's listed below:
Rule Name: Allow Inbox
Field: URI Path
Operator: contains
Value: /inbox
Log matching requests: On
Then take action...: Skip
WAF components to skip: All remaining custom rules
Click `Deploy' to complete
Caching Settings
From the main page -> Your DOMAINNAME.COM -> Caching -> Cache Rules -> Click Create rule -> Change the following settings on Cloudflare to match what's listed below:
Rule name: ActivityPub
Custom filter expressions: On
Field: URI Path
Operator: Starts with
Value: /activities/
Click Or
Repeat until you have values for 4 rules total containing the values:
/activities/
/api/
/nodeinfo/
/.well-known/webfinger
Cache Eligibility: On
Edge TTL -> Click + add setting
Click Ignore cache-control header and use this TTL
Input time-to-live (TTL): 2 hours
Click Deploy to complete
Click Create rule again
Rule name: ActivityPub2
Custom filter expressions: On
Field: Request Header
Name: accept
Operator: contains
Value: application/activity+json
Click Or
Repeat until you have 2 rules total containing the values:
application/activity+json
application/ld+json
Cache Eligibility: On
Edge TTL -> Click + add setting
Click Ignore cache-control header and use this TTL
Input time-to-live (TTL): Type 10 seconds
Click Deploy to complete
Optimization Settings
Speed -> Optimization -> Content Optimization -> Change the following settings on Cloudflare to match what's listed below:
Click Create Token -> Click Get Started under Create Custom Token
Token Name -> PieFed
Under Permissions -> Change the following drop down menu's to match what's listed below
First drop down menu: Zone
Second drop down menu: Cache Purge
Third drop down menu: Purge
Click Continue to summary -> Click Create Token
Copy the generated API Token. This will be used for CLOUDFLARE_API_TOKEN in the .env.docker file. Note, once you leave this screen, the API token will remain but the generated code that can be copied will disappear forever.
Copy API Zone ID
From the main page -> Your DOMAINNAME.COM -> Scroll down and look for API Zone ID in the far right column
Copy API Zone ID Token. This will be used for CLOUDFLARE_ZONE_ID in the .env.docker File.
The following step must be completed on the Raspberry Pi (LOCAL HOST) where PieFed is running:
nano ~/pyfedi/.env.docker
Add the following lines with your copied API Tokens & Save
CLOUDFLARE_API_TOKEN = 'ZONE.CACHE_PURGE_TOKEN'
CLOUDFLARE_ZONE_ID = 'API_ZONE_ID_TOKEN'
Restart PieFed Docker container
docker compose down && docker compose up -d
Troubleshooting
If you receive an error while posting images, the folder permissions will need to change. Change USERNAME with your username.
cd ~/pyfedi
sudo chown -R USERNAME:USERNAME ./media
Support and Services
Remote SSH Access Setup
With how Cloudflare works, SSH is not as simple and requires a bit more effort. I'm going to explain how to prepare Termux, an android terminal app, so you can access the Raspberry Pi remotely. The steps should be quite similar if you are using a Debian 12 distribution.
For remote SSH to work, you must provide a config file with some information. Fortunately, cloudflared will give you all the information you need for the config file.
A subdomain that will be used for the SSH connection will also need to be created. In this example I will simply use the subdomain ssh. which will look like this ssh.DOMAINNAME.COM.
The subdomain must be set up first before setting up the remote clients. You will use the Cloudflare Tunnel name (CLOUDFLARE_TUNNEL_NAME) that you previously created. Also note, the config file edited on the Raspberry Pi Local Host must be copied again to /etc/cloudflared before cloudflared is restarted.
The Cloudflare Tunnel name is the name you chose. Not to be confused with TUNNEL_ID which is a bunch of random letters and numbers that was generated for your tunnel.
For this example I'll use port 22 for SSH connections. This is the default port for SSH connections.
Raspberry Pi Setup (LOCAL HOST)
SSH login to the Raspberry Pi
cloudflared tunnel route dns CLOUDFLARE_TUNNEL_NAME ssh.DOMAINNAME.COM
nano ~/.cloudflared/config.yml
Paste the following and change TUNNEL_ID and DOMAINNAME.COM & Save
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 explaining rsync 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.
Nice! this is an idea that crosses my mind every other time, how is performance? do you think a PI0 would be able to run an instance just to host my own unique user? Any thoughts on the Lemmy-Easy-Deploy script?
I haven't had a chance to really test how Lemmy and PieFed work long term on the Pi 5 yet. So far it's been quick and responsive and I'm still using wifi instead of a direct ethernet connection to the main modem. Ethernet is for the future. I still have more work to finish on the Pi 5.
The Pi 5 is also running Kiwix, Dufs for file sharing and a static page. All run through their own docker containers. With only me using it, everything seems to run just quite smoothly.
My goals with the Pi 5 aren't long term. I'm using it more as a working example until I can get better equipment for hosting but that involves other plans for a local project I want to put my energy into now.
You'll definitely want to use a reliable type of USB media storage with good read and write speeds. An SD card won't do well considering these webapps are database heavy and will be constantly writing stuff.
Lemmy easy deploy seems interesting, if you can get caddy in that script to handle TLS encryption certificates, It should do nicely. I struggled with Let's Encrypt and went a different route for now.