The official docs recommend that you install Lemmy using Docker on Ubuntu.
I decided to run this instance on the cheapest hardware that I had lying around, which meant avoiding docker and squeezing everything into ~350MB of memory.
Somewhat stubbornly, I also chose to use Rocky 9 instead of Ubuntu because I like the Red Hat ecosystem. This decision ended up costing me a lot more time than anticipated, so I've recorded the journey here for any wayward travelers foolish enough to follow me down this road.
Maybe someday I'll consider packaging this knowledge into an RPM, or submitting a PR to add Red Hat support to the ansible playbooks.
Hosting a lemmy instance involves setting up a number of different services:
- Backend Server
- Image Host
- Web Client
- Reverse Proxy
Note: Commands beginning with #
must be ran as a super-user (e.g. root
).
Setup
Create a lemmy
user, for use with the various services:
# useradd lemmy -m --system
Install Cargo , which will be needed to compile everything:
# dnf install cargo
Backend Server
By the end of this section you will hopefully be running an instance of lemmy_server
that will listen locally on port 8536
.
Database
The backend server requires PostgreSQL 15. The rocky 9 repos only have version 13, so add the v15 repo:
# dnf install https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
Now install PostgreSQL and related libraries:
# dnf install postgresql15 postgresql15-server postgresql15-contrib pgcryptokey_15
Edit the PostgreSQL config file to tune the memory usage based on your system specs using the wonderful PGTune Tool:
# vim /var/lib/pgsql/15/data/postgresql.conf
For example, I use the following settings:
max_connections = 200
shared_buffers = 125MB
effective_cache_size = 375MB
maintenance_work_mem = 32000kB
checkpoint_completion_target = 0.9
wal_buffers = 3840kB
default_statistics_target = 100
random_page_cost = 1.1
effective_io_concurrency = 200
work_mem = 320kB
min_wal_size = 1GB
max_wal_size = 4GB
Start the database, then enable it so that it's always there after a reboot:
# systemctl start posgresql-15
# systemctl enable postgresql-15
Now, create a database and associated user for the lemmy backend, replacing $password
with a password of your choice (keep this handy for later).
sudo -iu postgres psql -c "CREATE USER lemmy SUPERUSER WITH PASSWORD '$password';"
sudo -iu postgres psql -c "CREATE DATABASE lemmy WITH OWNER lemmy;"
Lemmy Server
Install the necessary dependencies:
# dnf install openssl openssl-devel glibc-all-langpacks pkg-config libpq libpq-devel rustfmt protobuf-compiler
Build the lemmy server binary:
$ cargo install lemmy_server --locked
Copy the resulting binary to somewhere useful:
$ sudo cp ~/.cargo/bin/lemmy_server /usr/bin/
Create a minimal configuration file (see the docs for a complete list of options):
# vim /etc/lemmy/lemmy.hjson
{
database: {
password: "$password"
}
hostname: "$hostname"
bind: "127.0.0.1"
port: 8536
federation: {
enabled: true
}
setup: {
admin_username: "$admin_user"
admin_password: "$admin_password"
site_name: "$site_name"
}
tls_enabled: true
}
Make sure to replace $password
above with the database password that you created earlier (told you it would come in handy).
$hostname
should be replaced with the public-facing hostname that your instance will be exposed as, e.g. lemmy.zone
.
The $admin_user
and $admin_password
values should be replaced with the details used to log in to the server for the first time.
Replace $site_name
with the name to display in the top left when people visit your instance.
Make the config file readable by our lemmy user:
# chown -R lemmy:lemmy /etc/lemmy/
Create a systemd service file:
# vim /etc/systemd/system/lemmy-server.service
[Unit]
Description=Lemmy - A link aggregator for the fediverse
After=network.target
[Service]
User=lemmy
ExecStart=/usr/bin/lemmy_server
Environment=LEMMY_CONFIG_LOCATION=/etc/lemmy/lemmy.hjson
Environment=PICTRS_PATH=/var/lib/pictrs
Environment=PICTRS_ADDR=127.0.0.1:8080
Restart=on-failure
# Hardening
ProtectSystem=yes
PrivateTmp=true
MemoryDenyWriteExecute=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
Start the server and enable it so that it's always there after a reboot:
# systemctl start lemmy-server
# systemctl enable lemmy-server
Image Host
Lemmy uses pict-rs to host images. At the end of this section, you will hopefully have an instance listening locally on port 8080
.
Pict-rs requires ImageMagick 7, which unfortunately isn't provided by the Rocky 9 repos. Instead, we will need to install a portable app image by downloading it and moving it to somewhere useful:
# dnf install wget
$ wget https://download.imagemagick.org/ImageMagick/download/binaries/magick
$ sudo cp magick /usr/bin/
# chmod +x /usr/bin/magick
Note that the image requires fuse
to run, so we'll install that now along with the rest of our dependencies:
# dnf install fuse rust-s3 perl-Image-ExifTool
Build the pict-rs binary:
$ cargo install pict-rs --locked
Copy the resulting binary to somewhere useful:
$ sudo cp ~/.cargo/bin/pict-rs /usr/bin/
Create a directory that will be used to store the image data, and give ownership to our lemmy user:
# mkdir /var/lib/pictrs
# chown -R lemmy:lemmy /var/lib/pictrs
Create a systemd service file:
# vim /etc/systemd/system/pictrs.service
[Unit]
Description=pict-rs - A simple image host
After=lemmy-server.service
Before=nginx.service
[Service]
User=lemmy
WorkingDirectory=/var/lib/pictrs
Environment=RUST_LOG=info
ExecStart=/usr/bin/pict-rs run -a 127.0.0.1:8080 --media-format png filesystem -p data/files sled -p data/sled-repo
Restart=on-failure
[Install]
WantedBy=multi-user.target
Start the server and enable it so that it's always there after a reboot:
# systemctl start pictrs
# systemctl enable pictrs
Web Client
You'll need some form of client to access the server via a browser. We're going to install the official Lemmy UI and have it listen locally on port 8537
.
First, install the necessary dependencies:
# dnf install git nodejs yarnpkg
Create a new directory, and give ownership to our lemmy user:
# mkdir /var/lib/lemmy-ui
# chown -R lemmy:lemmy /var/lib/lemmy-ui
sudo
to the lemmy
user for the next few commands:
$ sudo -u lemmy -i
Move into our new directory, clone the lemmy-ui repository, and build it:
[lemmy@example ~] $ cd /var/lib/lemmy-ui
[lemmy@example lemmy-ui] $ git clone https://github.com/LemmyNet/lemmy-ui.git --recursive .
[lemmy@example lemmy-ui] $ yarn install --pure-lockfile
[lemmy@example lemmy-ui] $ yarn build:prod
You can now exit your sudo
session.
Create a systemd service file:
# vim /etc/systemd/system/lemmy-ui.service
[Unit]
Description=Lemmy UI - Web frontend for Lemmy
After=lemmy.service
Before=nginx.service
[Service]
User=lemmy
WorkingDirectory=/var/lib/lemmy-ui
ExecStart=/usr/bin/node dist/js/server.js
Environment=LEMMY_UI_HOST=127.0.0.1:8537
Environment=LEMMY_INTERNAL_HOST=127.0.0.1:8536
Environment=LEMMY_EXTERNAL_HOST=$hostname
Environment=LEMMY_HTTPS=true
Restart=on-failure
# Hardening
ProtectSystem=full
PrivateTmp=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
Replace $hostname
above with the public-facing hostname that your instance will be exposed as, e.g. lemmy.zone
.
Start the service and enable it so that it's always there after a reboot:
# systemctl start lemmy-ui
# systemctl enable lemmy-ui
Reverse Proxy
We now have all of our services running locally, so all that remains is to tie them together and make them accessible to the world using everyone's favourite reverse proxy, NGINX.
Install nginx, as well as certbot, which we will use to provision a free SSL certificate for your site.
# dnf install nginx certbot python3-certbot-nginx
Create a config file to describe the proxy setup:
# vim /etc/nginx/conf.d/lemmy.conf
Use the the official config, replacing {{domain}}
with the public-facing hostname that your instance will be exposed as, e.g. lemmy.zone
.
Replace {{lemmy_ui_port}}
with 8537
, and {{lemmy_port}}
with 8536
.
Generate an SSL certificate using certbot so that others may connect to your site securely:
# certbot certonly --nginx
Rocky 9 has SELinux enabled by default, so we need to change some settings to allow proxying of HTTP traffic to the internal ports that we are using:
# setsebool -P httpd_can_network_relay 1
# semanage port -a -t http_port_t -p tcp 8536
# semanage port -a -t http_port_t -p tcp 8537
Start nginx and enable it so that it's always there after a reboot:
# systemctl start nginx
# systemctl enable nginx
And there you have it! You should now be able to visit https://$hostname/
in your browser and be greeted by some wonderful open source, federated goodness.
Log in using the admin username and password that you set up earlier, and you're away.
Social engineering seems like it could be a bigger problem in the fediverse than on traditional social media platforms.
I feel that phishing becomes easier when there's no single authoritative site to log into, as people may not check the URL as thoroughly. Impersonation also seems problematic.
Like much of the early internet, this new tech seems reliant on trusting the goodwill of others. I'm sure in time we will see the platform evolve to counteract the bad actors.