⚠️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
  • A working docker install
  • 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:

  1. Replace default "http://lemmy-ui:1234"; at line 24 with default "http://lemmy-ui-two:3000";
  2. Add resolver 127.0.0.11; somewhere in the server{} block
  3. Replace proxy_pass "http://$lemmy_ui"; with proxy_pass "http://lemmy-ui-two:3000"; in the location = /.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;
    }
}

By youpie

One thought on “How to replace lemmy-ui”

Leave a Reply

Your email address will not be published. Required fields are marked *

Creative Commons License
Except where otherwise noted, the content on this site is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.