⚠️Warning I can not guarantee this tutorial is up to date or if this method still works ⚠️ It worked in december 2023
I did not like using the default lemmy-ui and wanted to change it to photon. But I could not find a tutorial that explained me how to do that.
Most Lemmy instances just add an extra domain so you can access Photon, but I wanted photon to be the main frontend for Lemmy, not a secondary one. It took quite a while to figure out, but i managed to do it and now I wanted to share my solution so my work wouldn’t go to waste.
Prerequisites
- A working computer
- I use Fedora server 38
- A working docker install
- I like using it with portainer
- A working reverse proxy
- I use Nginx which I run on my router, I think it only works on Linux and FreeBSD
- A domain name
Setting up Lemmy
Skip this if you already have a working lemmy instance
To set-up Lemmy in a docker container I obviously recommend following the official guide. However I found the new docker compose file they are using really confusing, you can find the one I based my final one on at Docker Compose Template.
I also provided the Lemmy.hjson file Template if you want to copy what i did exactly.
Then just create a DNS records for your lemmy instance, generate a certificate for the domain and create a configuration file for the reverse proxy to the lemmy port (I’m not gonna explain how to do that here, maybe I’ll write a tutorial about that some day). Type docker compose up -d
and if everything went right, you should now have a working lemmy instance with the old lemmy.ui
Replacing the UI
There are two steps that need to be taken to replace the ui, they are:
Editing docker-compose.yml file
Open the docker-compose.yml file for the lemmy instance and add the following lines to the services:
lemmy-ui-two:
image: ghcr.io/xyphyn/photon:latest
hostname: lemmy-ui-two
environment:
- PUBLIC_INTERNAL_INSTANCE=lemmy:8536
- PUBLIC_INSTANCE_URL={Website adress you are hosting it on (without https://)}
depends_on:
- lemmy
restart: always
logging: *default-logging
This will create an Photon container inside the Lemmy stack.
Editing the nginx_internal.conf file
The next thing to do is to open the nginx_internal.conf file, this file is the configuration file that the nginx instance used for Lemmy uses. Here you need to change 3 things:
- Replace
default "http://lemmy-ui:1234";
at line 24 withdefault "http://lemmy-ui-two:3000";
- Add
resolver 127.0.0.11;
somewhere in theserver{}
block - Replace
proxy_pass "http://$lemmy_ui";
withproxy_pass "http://lemmy-ui-two:3000";
in thelocation = /.well-known/security.txt {}
block
Now just docker compose up -d
and it should pull the latest version of photon and once it’s up and running you should be able to visit and use the new UI. There is just one issue
No server icons
You might notice that in the server icon is not showing up in photon. I ran into this issue and it was quite a struggle to fix.
Fixing the instance picture
You might notice it is not possible to edit the server icon from photon. That is why we still need the original Lemmy-ui, in my docker-compose template you can see it is still included. The Lemmy-ui also needs to be accessible to the outside, so create another entry in your reverse proxy and create a new subdomain (i used https://lemmyui.emphisia.nl). Visit the lemmy-ui and upload and save the preferred server icon.
Next open the page inspector (ctrl+shift+i
), btw i use Firefox but i assume chrome works mostly the same. Go to the network tab and reload the page. On this tab you can see all the network requests made to the server. Search through all the requests look at the images. You need to search for the request of the image that is server icon.
If the image is only 96x96
pixels, like in the example. Click on the request and look at the filename in the right tab. Copy the filename. This is where your server icon is stored. In my case this is /pictrs/image/4aa51995-03ff-4e43-94af-c4eeea481462.png
.
Now open your reverse proxy configuration for Lemmy. Photon tries to access /logo-background.svg
to get the background. So we need make that URL point to the image. To do this in NGINX I used the following configuration:
location /img/logo-background.svg/{
proxy_pass http://0.0.0.0:8536/pictrs/image/4aa51995-03ff-4e43-94af-c4eeea481462.png;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
Now restart NGINX, and if everything went well, the server icon should now work! That was epic. If it does not work for whatever reason, the page inspector is your best friend.
I also added my NGINX configuration to this page as inspiration.
If you have any questions, feel free to contact me!
Lemmy.hjson file Template
{
database: {
host: postgres
password: "{Just make something up}"
}
hostname: "{Website adress you are hosting it on (without https://)}"
pictrs: {
url: "http://pictrs:8080"
api_key: "{Just make something up}"
}
email: {
smtp_server: "mail.emphisia.nl:465"
smtp_login: "xxx"
smtp_password: "XXX"
smtp_from_address: "[email protected]"
tls_type: "tls"
}
}
Docker Compose Template
version: "3.7"
x-logging: &default-logging
driver: "json-file"
options:
max-size: "50m"
max-file: "4"
services:
proxy:
image: nginx:1-alpine
ports:
# actual and only port facing any connection from outside
# Note, change the left number if port 1236 is already in use on your system
# You could use port 80 if you won't use a reverse proxy
- "{Port that lemmy will use}:8536"
volumes:
- ./nginx_internal.conf:/etc/nginx/nginx.conf:ro,Z
restart: always
logging: *default-logging
depends_on:
- pictrs
- lemmy-ui
lemmy:
image: dessalines/lemmy:latest
hostname: lemmy
restart: always
logging: *default-logging
environment:
- RUST_LOG="warn"
volumes:
- ./lemmy.hjson:/config/config.hjson:Z
depends_on:
- postgres
- pictrs
lemmy-ui:
image: dessalines/lemmy-ui:latest
environment:
- LEMMY_UI_LEMMY_INTERNAL_HOST={Local IP of PC}:{Lemmy Port}
- LEMMY_UI_LEMMY_EXTERNAL_HOST={Website adress you are hosting it on (without https://)}
- LEMMY_UI_HTTPS=false
volumes:
- ./volumes/lemmy-ui/extra_themes:/app/extra_themes
depends_on:
- lemmy
restart: always
logging: *default-logging
pictrs:
image: asonix/pictrs
# this needs to match the pictrs url in lemmy.hjson
hostname: pictrs
# we can set options to pictrs like this, here we set max. image size and forced format for conversion
# entrypoint: /sbin/tini -- /usr/local/bin/pict-rs -p /mnt -m 4 --image-format webp
environment:
- PICTRS_OPENTELEMETRY_URL=http://otel:4137
- PICTRS__SERVER__API_KEY={PICTRS API KEY FROM CONFIG}
- RUST_LOG=debug
- RUST_BACKTRACE=full
- PICTRS__MEDIA__VIDEO_CODEC=vp9
- PICTRS__MEDIA__GIF__MAX_WIDTH=256
- PICTRS__MEDIA__GIF__MAX_HEIGHT=256
- PICTRS__MEDIA__GIF__MAX_AREA=65536
- PICTRS__MEDIA__GIF__MAX_FRAME_COUNT=400
user: 991:991
volumes:
- ./volumes/pictrs:/mnt:Z
restart: always
logging: *default-logging
deploy:
resources:
limits:
memory: 690m
postgres:
image: postgres:15-alpine
hostname: postgres
environment:
- POSTGRES_USER=lemmy
- POSTGRES_PASSWORD={POSTGRESS PASSWORD FROM CONFIG}
- POSTGRES_DB=lemmy
volumes:
- ./volumes/postgres:/var/lib/postgresql/data:Z
- ./customPostgresql.conf:/etc/postgresql.conf
restart: always
logging: *default-logging
postfix:
image: mwader/postfix-relay
environment:
- POSTFIX_myhostname={Website adress you are hosting it on (without https://)}
restart: "always"
logging: *default-logging
NGINX configuration
limit_req_zone $binary_remote_addr zone=lemmy.emphisia.nl_ratelimit:10m rate=1r/s;
server {
if ($host = lemmy.emphisia.nl) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
listen [::]:80;
server_name lemmy.emphisia.nl;
# Hide nginx version
server_tokens off;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name lemmy.emphisia.nl;
ssl_certificate /etc/letsencrypt/live/lemmy.emphisia.nl/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/lemmy.emphisia.nl/privkey.pem; # managed by Certbot
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-G>
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets on;
ssl_stapling on;
ssl_stapling_verify on;
# Hide nginx version
server_tokens off;
# Upload limit, relevant for pictrs
client_max_body_size 20M;
# Enable compression for JS/CSS/HTML bundle, for improved client load times.
# It might be nice to compress JSON, but leaving that out to protect against potential
# compression+encryption information leak attacks like BREACH.
gzip on;
gzip_types text/css application/javascript image/svg+xml;
gzip_vary on;
# Various content security headers
add_header Referrer-Policy "same-origin";
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
add_header X-XSS-Protection "1; mode=block";
location / {
proxy_pass http://0.0.0.0:8536;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /img/logo-background.svg/{
proxy_pass http://0.0.0.0:8536/pictrs/image/4aa51995-03ff-4e43-94af-c4eeea481462.png;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Hella niceselaa