Installing Lemmy on YunoHost (Docker Method)
This guide documents the process of installing Lemmy (a federated link aggregator) on a VPS running YunoHost, utilizing Docker Compose. This "hybrid" approach allows Docker to manage the application dependencies while letting YunoHost handle the domain, DNS, and SSL certificates via a standard Reverse Proxy.
Prerequisites
- OS: Debian (via YunoHost 11.x or newer).
- Software: Docker & Docker Compose installed.
- Domain: A dedicated subdomain (e.g.,
lemmy.nulu.my) added to the YunoHost admin panel. - Access: SSH root/sudo access.
1. Domain Preparation
Before touching Docker, ensure the domain is ready in YunoHost.
- Log in to the YunoHost Web Admin.
- Navigate to Domains > Add Domain.
- Add your subdomain (e.g.,
lemmy.nulu.my). - Navigate to Tools > Diagnosis to ensure DNS records are propagated (specifically the A record pointing to your VPS IP).
2. File Structure Setup
Create a dedicated directory to keep the installation clean.
# Create project directories
mkdir -p ~/docker/lemmy/config
mkdir -p ~/docker/lemmy/nginx
mkdir -p ~/docker/lemmy/volumes/postgres
mkdir -p ~/docker/lemmy/volumes/pictrs
cd ~/docker/lemmy
3. Configuration Files
We need three main files: lemmy.hjson (app config), nginx.conf (internal proxy), and docker-compose.yml.
A. Lemmy Configuration (config/lemmy.hjson)
Create the file: nano config/lemmy.hjson
{
database: {
host: "postgres"
password: "YOUR_DB_PASSWORD"
user: "lemmy"
database: "lemmy"
}
hostname: "lemmy.nulu.my"
pictrs: {
url: "http://pictrs:8080"
api_key: "YOUR_PICTRS_API_KEY"
}
setup: {
admin_username: "your_username"
admin_password: "YOUR_ADMIN_PASSWORD"
site_name: "My Lemmy Instance"
admin_email: "email@example.com"
}
}
Critical Note: Do not include allow_signups: true in the setup block. This setting is deprecated in config files and will cause the container to crash with a "Main Panic" error. This setting is now managed via the Web UI.
B. Internal Nginx Proxy (nginx/nginx.conf)
This internal proxy routes traffic between the frontend (UI) and the backend (API), presenting a single port (8536) to YunoHost.
Create the file: nano nginx/nginx.conf
events { worker_connections 1024; }
http {
upstream lemmy { server lemmy:8536; }
upstream lemmy-ui { server lemmy-ui:1234; }
server {
listen 80;
server_name lemmy.nulu.my;
# Frontend (The UI)
location / {
proxy_pass http://lemmy-ui;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# Backend (The API and Images)
location ~ ^/(api|pictrs|feeds|nodeinfo|.well-known) {
proxy_pass http://lemmy;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# Websocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}
C. Docker Compose (docker-compose.yml)
Create the file: nano docker-compose.yml
services:
proxy:
image: nginx:1-alpine
hostname: proxy
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
# Binds to local port 8536. YunoHost will proxy to this.
ports:
- "127.0.0.1:8536:80"
depends_on:
- lemmy
- lemmy-ui
restart: always
lemmy:
image: dessalines/lemmy:0.19.3
hostname: lemmy
restart: always
volumes:
- ./config/lemmy.hjson:/config/config.hjson
depends_on:
- postgres
- pictrs
logging:
driver: "json-file"
options:
max-size: "50m"
lemmy-ui:
image: dessalines/lemmy-ui:0.19.3
hostname: lemmy-ui
restart: always
environment:
- LEMMY_UI_LEMMY_INTERNAL_HOST=lemmy:8536
- LEMMY_UI_LEMMY_EXTERNAL_HOST=lemmy.nulu.my
- LEMMY_UI_HTTPS=true
depends_on:
- lemmy
postgres:
image: postgres:15-alpine
hostname: postgres
environment:
- POSTGRES_USER=lemmy
- POSTGRES_PASSWORD=YOUR_DB_PASSWORD
- POSTGRES_DB=lemmy
volumes:
- ./volumes/postgres:/var/lib/postgresql/data
restart: always
pictrs:
image: asonix/pictrs:0.5
hostname: pictrs
volumes:
- ./volumes/pictrs:/mnt
environment:
- PICTRS__SERVER__API_KEY=YOUR_PICTRS_API_KEY
restart: always
logging:
driver: "json-file"
options:
max-size: "50m"
4. Permission Fixes
Docker containers often run as specific non-root users (Postgres uses UID 70 or 999; Pictrs uses UID 991). If folders are created by root, the containers will fail to start.
Run these commands to fix ownership:
sudo chown -R 991:991 volumes/pictrs
sudo chown -R 70:70 volumes/postgres
5. Deployment
Launch the stack:
docker compose up -d
Check logs to ensure stability:
docker compose logs -f
6. YunoHost Integration (The "Redirect" App)
Now that Lemmy is running locally on port 8536, expose it via YunoHost.
- Go to YunoHost Admin > Applications > Install.
- Select Redirect.
- Configuration:
- Domain:
lemmy.nulu.my - Path:
/ - Type:
Proxy(Important!) - Destination:
http://127.0.0.1:8536
- Click Install.
YunoHost will now generate the SSL certificate via Let's Encrypt and route incoming traffic to your Docker container.
Troubleshooting / Lessons Learnt
- 502 Bad Gateway: Usually means the container is crashing. Check logs with
docker compose logs. - Panic "Unknown field
allow_signups": Thelemmy.hjsonformat changed in recent versions. Removeallow_signupsfrom the config file and manage it in the UI. - Raw JSON instead of UI: If you see raw code when visiting the site, you are hitting the API directly. Ensure you have the
lemmy-uiservice running and the internal Nginx proxy correctly routing/requests tolemmy-uiand/apirequests tolemmy.
No comments to display
No comments to display