Lemmy is a selfhosted, federated social link aggregation and discussion forum. It consists of many different communities which are focused on different topics. Users can post text, links or images and discuss it with others. Voting helps to bring the most interesting items to the top. There are strong moderation tools to keep out spam and trolls. All this is completely free and open, not controlled by any company. This means that there is no advertising, tracking, or secret algorithms.

Federation is a form of decentralization. Instead of a single central service that everyone uses, there are multiple services that any number of people can use.

A Lemmy website can operate alone. Just like a traditional website, people sign up on it, post messages, upload pictures and talk to each other. Unlike a traditional website, Lemmy instances can interoperate, letting their users communicate with each other; just like you can send an email from your Gmail account to someone from Outlook, Fastmail, Proton Mail, or any other email provider, as long as you know their email address, you can mention or message anyone on any website using their address.

Lemmy uses a standardized, open protocol to implement federation which is called ActivityPub. Any software that likewise implements federation via ActivityPub can seamlessly communicate with Lemmy, just like Lemmy instances communicate with one another.

The fediverse ("federated universe") is the name for all instances that can communicate with each other over ActivityPub and the World Wide Web. That includes all Lemmy servers, but also other implementations:

In practical terms: Imagine if you could follow a Facebook group from your Reddit account and comment on its posts without leaving your account. If Facebook and Reddit were federated services that used the same protocol, that would be possible. With a Lemmy account, you can communicate with any other compatible instance, even if it is not running on Lemmy. All that is necessary is that the software support the same subset of the ActivityPub protocol.

Unlike proprietary services, anyone has the complete freedom to run, examine, inspect, copy, modify, distribute, and reuse the Lemmy source code. Just like how users of Lemmy can choose their service provider, you as an individual are free to contribute features to Lemmy or publish a modified version of Lemmy that includes different features. These modified versions, also known as software forks, are required to also uphold the same freedoms as the original Lemmy project. Because Lemmy is libre software that respects your freedom, personalizations are not only allowed but encouraged.

You can contribute to this documentation in the git repository.

This page is adapted from Mastodon documentation under CC BY-SA 4.0.

Choosing an instance

If you are used to sites like Reddit, then Lemmy works in a fundamentally different way. Instead of a single website like, there are many different websites (called instances). These are operated by different people, have different topics and rules. Nevertheless, posts created in one instance can directly be seen by users who are registered on another. Its basically like email, but for social media.

This means before using Lemmy and registering an account, you need to pick an instance. For this you can browse the instance list and look for one that matches your topics of interest. You can also see if the rules match your expectations, and how many users there are. It is better to avoid very big or very small instances. But don't worry too much about this choice, you can always create another account on a different instance later.

[instance list screenshot]


Once you choose an instance, it's time to create your account. To do this, click sign up in the top right of the page, or click the top right button on mobile to open a menu with sign up link.

[registration page screenshot]

On the signup page you need to enter a few things:

  • Username: How do you want to be called? This name can not be changed and is unique within an instance. Later you can also set a displayname which can be freely changed. If your desired username is taken, consider choosing a different instance where it is still available.
  • Email: Your email address. This is used for password resets and notifications (if enabled). Providing an email address is usually optional, but admins may choose to make it mandatory. In this case you will have to wait for a confirmation mail and click the link after completing this form.
  • Password: The password for logging in to your account. Make sure to choose a long and unique password which isn't used on any other website.
  • Verify password: Repeat the same password from above to ensure that it was entered correctly.

There are also a few optional fields, which you may need to fill in depending on the instance configuration:

  • Question/Answer: Instance admins can set an arbitrary question which needs to be answered in order to create an account. This is often used to prevent spam bots from signing up. After submitting the form, you will need to wait for some time until the answer is approved manually before you can login.
  • Code: A captcha which is easy to solve for humans but hard for bots. Enter the letters and numbers that you see in the text box, ignoring uppercase or lowercase. Click the refresh button if you are unable to read a character. The play button plays an audio version of the captcha.
  • Show NSFW content: Here you can choose if content that is "not safe for work" (or adult-only) should be shown.

When you are done, press the sign up button.

It depends on the instance configuration when you can login and start using the account. In case the email is mandatory, you need to wait for the confirmation email and click the link first. In case "Question/Answer" is present, you need to wait for an admin to manually review and approve your registration. If you have problems with the registration, try to get in contact with the admin for support. You can also choose a different instance to sign up if your primary choice does not work.

Following communities

After logging in to your new account, its time to follow communities that you are interested in. For this you can click on the communities link at the top of the page (on mobile, you need to click the menu icon on the top right first). You will see a list of communities which can be filtered by subscribed, local or all. Local communities are those which are hosted on the same site where you are signed in, while all also contains federated communities from other instances. In any case you can directly subscribe to communities with the right-hand subscribe link. Or click on the community name to browse the community first, see what its posted and what the rules are before subscribing.

Another way to find communities to subscribe to is by going to the front page and browsing the posts. If there is something that interests you, click on the post title to see more details and comments. Here you can subscribe to the community in the right-hand sidebar, or by clicking the "sidebar" button on mobile.

These previous ways will only show communities that are already known to the instance. Especially if you joined a small or inactive Lemmy instance, there will be few communities to discover. You can find more communities by browsing different Lemmy instances, or using the Lemmy Community Browser. When you found a community that you want to follow, enter its URL (e.g. or the identifier (e.g. ! into the search field of your own Lemmy instance. Lemmy will then fetch the community from its original instance, and allow you to interact with it. The same method also works to fetch users, posts or comments from other instances.

Setting up your profile

Before you start posting, its a good idea to provide some details about yourself. Open the top-right menu and go to "settings". Here the following settings are available for your public profile:

  • Displayname: An alternative username which can be changed at any time
  • Bio: Long description of yourself, can be formatted with Markdown
  • Matrix User: Your username on the decentralized Matrix chat
  • Avatar: Profile picture that is shown next to all your posts
  • Banner: A header image for your profile page

On this page you can also change the email and password. Additionally there are many other settings available, which allow customizing of your browsing experience:

  • Blocks (tab at top of the page): Here you can block users and communities, so that their posts will be hidden.
  • Interface language: Which language the user interface should use.
  • Languages: Select the languages that you speak to see only content in these languages. This is a new feature and many posts don't specify a language yet, so be sure to select "Undetermined" to see them.
  • Theme: You can choose between different color themes for the user interface. Instance admins can add more themes.
  • Type: Which timeline you want to see by default on the frontpage; only posts from communities that you subscribe to, posts in local communities, or all posts including federated.
  • Sort type: How posts and comments should be sorted by default. See Votes and Ranking for details.
  • Show NSFW content: Whether or not you want to see content that is "not safe for work" (or adult-only).
  • Show Scores: Whether the number of upvotes and downvotes should be visible.
  • Show Avatars: Whether profile pictures of other users should be shown.
  • Bot Account: Enable this if you are using a script or program to create posts automatically
  • Show Bot Accounts: Disable this to hide posts that were created by bot accounts.
  • Show Read Posts: If this is disabled, posts that you already viewed are not shown in listings anymore. Useful if you want to find new content easily, but makes it difficult to follow ongoing discussion under existing posts.
  • Show Notifications for New Posts: Enable this to receive a popup notification for each new post that is created.
  • Send notifications to Email: Enable to receive notifications about new comment replies and private messages to your email address.

Start posting

Finally its time to start posting! To do this it is always a good idea to read the community rules in the sidebar (below the Subscribe button). When you are ready, go to a post and type your comment in the box directly below for a top-level reply. You can also write a nested reply to an existing comment, by clicking the left-pointing arrow.

Other than commenting on existing posts, you can also create new posts. To do this, click the button Create a post in the sidebar. Here you can optionally supply an external link or upload an image. The title field is mandatory and should describe what you are posting. The body is again optional, and gives space for long texts. You can also embed additional images here. The Community dropdown below allows choosing a different community to post in. With NSFW posts can be marked as "not safe for work". Finally you can specify the language that the post is written in, and then click on Create.

One more possibility is to write private messages to individual users. To do this, simply visit a user profile and click Send message. You will be notified about new private messages and comment replies with the bell icon in the top right.


The main type of content in Lemmy is text which can be formatted with Markdown. Refer to the table below for supported formatting rules. The Lemmy user interface also provides buttons for formatting, so it's not necessary to remember all of it. You can also follow the interactive CommonMark tutorial to get started.

TypeOr… to Get
# Heading 1Heading 1

Heading 1

## Heading 2Heading 2
Heading 2


[1]: http://url/b.jpg
> Blockquote
* List
* List
* List
- List
- List
- List
_ List
_ List
* List
1. One
2. Two
3. Three
1) One
2) Two
3) Three
1. One
2. Two
3. Three
Horizontal Rule
Horizontal Rule
Horizontal Rule

`Inline code` with backticksInline code with backticks
# code block
print '3 backticks or'
print 'indent 4 spaces'
····# code block
····print '3 backticks or'
····print 'indent 4 spaces'
# code block
print '3 backticks or'
print 'indent 4 spaces'
::: spoiler hidden or nsfw stuff
a bunch of spoilers here
hidden or nsfw stuff

a bunch of spoilers here

Some subscript textSome subscript text
Some ^superscript^ textSome superscript text

CommonMark Tutorial

Images and video

Lemmy also allows sharing of images and videos. To upload an image, go to the Create post page and click the little image icon under the URL field. This allows you to select a local image. If you made a mistake, a popup message allows you to delete the image. The same image button also allows uploading of videos in .gif format. Instead of uploading a local file, you can also simply paste the URL of an image or video from another website.

Note that this functionality is not meant to share large images or videos, because that would require too many server resources. Instead, upload them on another platform like PeerTube or Pixelfed, and share the link on Lemmy.

Votes and ranking

Lemmy uses a voting system to sort post listings. On the left side of each post there are up and down arrows, which let you upvote or downvote it. You can upvote posts that you like so that more users will see them. Or downvote posts so that they are less likely to be seen. Each post receives a score which is the number of upvotes minus number of downvotes.

When browsing the frontpage or a community, you can choose between the following sort types for posts:

  • Active (default): Calculates a rank based on the score and time of the latest comment, with decay over time
  • Hot: Like active, but uses time when the post was published
  • Scaled: Like hot, but gives a boost to less active communities
  • New: Shows most recent posts first
  • Old: Shows oldest posts first
  • Most Comments: Shows posts with highest number of comments first
  • New Comments: Bumps posts to the top when they are created or receive a new reply, analogous to the sorting of traditional forums
  • Top Day: Highest scoring posts during the last 24 hours
  • Top Week: Highest scoring posts during the last 7 days
  • Top Month: Highest scoring posts during the last 30 days
  • Top Year: Highest scoring posts during the last 12 months
  • Top All Time: Highest scoring posts during all time

Comments are by default arranged in a hierarchy which shows at a glance who it is replying to. Top-level comments which reply directly to a post are on the very left, not indented at all. Comments that are responding to top-level comments are indented one level and each further level of indentation means that the comment is deeper in the conversation. With this layout it is always easy to see the context for a given comment, by simply scrolling up to the next comment which is indented one level less.

Comments can be sorted in the following ways. These all keep the indentation intact, so only replies to the same parent are shuffled around.

  • Hot (default): Equivalent to the Hot sort for posts
  • Top: Shows comments with highest score first
  • New: Shows most recent comments first
  • Old: Shows oldest comments first

Additionally there is a sort option Chat. This eliminates the hierarchy, and puts all comments on the top level, with newest comments shown at the top. It is useful to see new replies at any point in the conversation, but makes it difficult to see the context.

The ranking algorithm is described in detail here.


The internet is full of bots, trolls and other malicious actors. Sooner or later they will post unwanted content to any website that is open to the public. It is the task of administrators and moderators to remove such unwanted content. Lemmy provides many tools for this, from removing individual posts, over temporary bans, to removing all content from an offending user.

Moderation in Lemmy is divided between administrators and moderators. Admins are responsible for the entire instance, and can take action on any content. They are also the only ones who can completely ban users. In contrast, moderators are only responsible for a single community. Where admins can ban a user from the entire instance, mods can only ban them from their community.

The most important thing that normal users can do if they notice a rule breaking post is to use the report function. If you notice such a post, click the flag icon to notify mods and admins. This way they can take action quickly and remove the offending content. To find out about removals and other mod actions, you can use the mod log which is linked at the bottom of the page. In some cases there may be content that you personally dislike, but which doesn't violate any rules. For this exists a block function which hides all posts from a given user or community.

Each instance has a set of rules to let users know which content is allowed or not. These rules can be found in the sidebar and apply to all local communities on that instance. Some communities may have their own rules in the respective sidebar, which apply in addition to the instance rules.

Because Lemmy is decentralized, there is no single moderation team for the platform, nor any platform-wide rules. Instead each instance is responsible to create and enforce its own moderation policy. This means that two Lemmy instances can have rules that completely disagree or even contradict. This can lead to problems if they interact with each other, because by default federation is open to any instance that speaks the same protocol. To handle such cases, administrators can choose to block federation with specific instances. To be even safer, they can also choose to be federated only with instances that are allowed explicitly.

How to moderate

To get moderator powers, you either need to create a new community, or be appointed by an existing moderator. Similarly to become an admin, you need to create a new instance, or be appointed by an existing instance admin. Community moderation can be done over federation, you don't need to be registered on the same instance where the community is hosted. To be an instance administrator, you need an account on that specific instance. Admins and moderators are organized in a hierarchy, where the user who is listed first has the power to remove admins or mods who are listed later.

All moderation actions are taken on the context menu of posts or comments. Click the three dot button to expand available mod actions, as shown in the screenshot below. All actions can be reverted in the same way.

moderation_01.png moderation_02.png

ActionResultPermission level
LockPrevents making new comments under the postModerator
Sticky (Community)Pin the publication to the top of the community listingModerator
Sticky (Local)Pin the publication to the top of the frontpageAdmin
RemoveDelete the postModerator
Ban from communityBan user from interacting with the community, but can still use the rest of the site. There is also an option to remove all existing posts.Moderator
Appoint as modGives the user moderator statusModerator
Ban from siteCompletely bans the account, so it can't login or interact at all. There is also an option to remove all existing posts.Admin
Purge userCompletely delete the user, including all posts and uploaded media. Use with caution.Admin
Purge post/commentCompletely delete the post, including attached media.Admin
Appoint as adminGives the user administrator statusAdmin

Censorship resistance

Today's social media landscape is extremely centralized. The vast majority of users are concentrated on only a handful of platforms like Facebook, Reddit or Twitter. All of these are maintained by large corporations that are subject to profit motive and United States law. In recent years these platforms have increasingly censored users and entire communities, often with questionable justifications. It is only natural that those who are affected by this search for alternatives. This document is intended to help with the evaluation.

For this purpose we will consider as censorship anything that prevents a person from expressing their opinion, regardless of any moral considerations. All the options explained here also have legitimate uses, such as deleting spam. Nevertheless it is important for users to understand why their posts are getting removed and how to avoid it.

The first and most common source of censorship in this sense is the admin of a given Lemmy instance. Due to the way federation works, an admin has complete control over their instance, and can arbitrarily delete content or ban users. The moderation log helps to provide transparency into such actions.

The second source of censorship is through legal means. This often happens for copyright violation, but can also be used for other cases. What usually happens in this case is that the instance admin receives a takedown notice from the hosting provider or domain registrar. If the targeted content is not removed within a few days, the site gets taken down. The only way to avoid this is to choose the hosting company and country carefully, and avoid those which might consider the content as illegal.

Another way to censor is through social pressure on admins. This can range from spamming reports for unwanted content, to public posts from influential community members demanding to take certain content down. Such pressure can keep mounting for days or weeks, making it seem like everyone supports these demands. But in fact it is often nothing more than a vocal minority. It is the task of admins to gauge the true opinion of their community. Community members should also push back if a minority tries to impose its views on everyone else.

All of this shows that it is relatively easy to censor a single Lemmy instance. Even a group of instances can be censored if they share the same admin team, hosting infrastructure or country. Here it is important that an admin can only censor content on their own instance, or communities which are hosted on his instance. Other instances will be unaffected. So if there is a problem with censorship, it can always be solved by using a different Lemmy instance, or creating a new one.

But what if the goal was to censor the entire Lemmy network? This is inherently difficult because there is no single entity which has control over all instances. The closest thing to such an entity are the developers, because they can make changes to the code that all the instances run. For example, developers could decide to implement a hardcoded block for certain domains, so that they can't federate anymore. However, changes need to be released and then installed by instance admins. Those who are affected would have no reason to upgrade. And because the code is open source, they could publish a forked software version without these blocks. So the effect would be very limited, but it would split the project and result in loss of reputation for the developers. This is probably the reason why it has never happened on any Fediverse platform.

Lastly it might be possible to abuse software vulnerabilities for network-wide censorship. Imagine a bug in Lemmy or in the underlying software stack which allows the attacker to delete arbitrary content. This could remain undetected for a while if used sparingly, but would certainly be discovered after some time. And experience has shown that such critical flaws are fixed very quickly in open source software. It is also highly unlikely that critical vulnerabilities be present in multiple different Fediverse platforms at the same time.

In conclusion, the best way to avoid censorship on Lemmy is through the existence of many independent instances. These should have different admins, different hosting providers and be located in different countries. Additionally users should follow the development process to watch for changes that might create a centralized point of control for all instances. Based on this explanation it should be clear that censorship on Lemmy is difficult, and can always be circumvented. This is in contrast to centralized platforms like Facebook or Reddit. They are not open source and can't be self-hosted, so it is necessary to switch to an entirely different platform to avoid censorship. And due to lack of federation, such a switch means losing contact with users who decide to stay on the censored platform.


Users can choose between a number of built-in color themes. Admins can also provide additional themes and set them as default.

Easy to install, low hardware requirements

Lemmy is written in Rust, which is an extremely fast language. Thats why it has very low hardware requirements. It can easily run on a Raspberry Pi or similar low-powered hardware. This makes it easy to administrate and keeps costs low.

Language Tags

Lemmy instances and communities can specify which languages can be used for posting. Consider an instance aimed at Spanish users, it would limit the posting language to Spanish so that other languages can't be used. Or an international instance which only allows languages that the admin team understands. Community languages work in the same way, and are restricted to a subset of the instance languages. By default all languages are allowed (including undefined).

Users can also specify which languages they speak, and will only see content in those languages. Lemmy tries to smartly select a default language for new posts if possible. Otherwise you have to specify the language manually.

Lemmy as a blog

Lemmy can also function as a blogging platform. Doing this is as simple as creating a community and enabling the option "Only moderators can post to this community". Now only you and other people that you invite can create posts, while everyone else can comment. Like any Lemmy community, it is also possible to follow from other Fediverse platforms and over RSS. For advanced usage it is even possible to use the API and create a different frontend which looks more blog-like.

History of Lemmy

The idea to make Lemmy was a combination of factors.

Open source developers like myself have long watched the rise of the “Big Five”, the US tech giants that have managed to capture nearly all the world’s everyday communication into their hands. We’ve been asking ourselves why people have moved away from content-focused sites, and what we can do to subvert this trend, in a way that is easily accessible to a non-tech focused audience.

The barriers to entry on the web are much lower than say in the physical world: all it takes is a computer and some coding knowhow… yet the predominant social media firms have been able to stave off competition for at least two reasons: their sites are easy to use, and they have huge numbers of users already (the “first mover” advantage). The latter is more important; if you’ve ever tried to get someone to use a different chat app, you’ll know what I mean.

Now I loved early Reddit, not just for the way that it managed to put all the news for the communities and topics I wanted to see in a single place, but for the discussion trees behind every link posted. I still have many of these saved, and have gained so much more from the discussion behind the links, than I have from the links themselves. In my view, its the community-focused, tree-like discussions, as well as the ability to make, grow, and curate communities, that has made Reddit the 5th most popular site in the US, and where so many people around the world get their news.

But that ship sailed years ago; the early innovative spirit of Reddit left with Aaron Swartz: its libertarian founders have allowed some of the most racist and sexist online communities to fester on Reddit for years, only occasionally removing them when community outcry reaches a fever pitch. Reddit closed its source code years ago, and the Reddit redesign has become a bloated anti-privacy mess.

Its become absorbed into that silicon valley surveillance-capitalist machine that commodifies users to sell ads and paid flairs, and propagandizes pro-US interests above all. Software technology being one of the last monopoly exports the US has, it would be naive to think that one of the top 5 most popular social media sites, where so many people around the world get their news, would be anything other than a mouthpiece for the interests of those same US coastal tech firms.

Despite the conservative talking point that big tech is dominated by “leftist propaganda”, it is liberal, and pro-US, not left (leftism referring to the broad category of anti-capitalism). Reddit has banned its share of leftist users and communities, and the Reddit admins via announcement posts repeatedly vilify the US’s primary foreign-policy enemies as having “bot campaigns”, and “manipulating Reddit”, yet the default Reddit communities (/r/news, /r/pics, etc), who share a small number of moderators, push a line consistent with US foreign-policy interests. The aptly named /r/copaganda subreddit has exposed the pro-police propaganda that always seems to hit Reddit’s front page in the wake of every tragedy involving US police killing the innocent (or showing police kissing puppies, even though US police kill ~ 30 dogs every day, which researchers have called a “noted statistical phenomenon”).

We’ve also seen a rise in anti-China posts that have hit Reddit lately, and along with that comes anti-chinese racism, which Reddit tacitly encourages. That western countries are seeing a rise in attacks against Asian-Americans, just as some of the perpetrators of several hate-crimes against women were found to be Redditors active in mens-rights Reddit communities, is not lost on us, and we know where these tech companies really stand when it comes to violence and hate speech. Leftists know that our position on these platforms is tenuous at best; we’re currently tolerated, but that will not always be the case.

The idea for making a Reddit alternative seemed pointless, until Mastodon (a federated twitter alternative), started becoming popular. Using Activitypub (a protocol / common language that social media services can use to speak to each other), we finally have a solution to the “first mover” advantage: now someone can build or run a small site, but still be connected to a wider universe of users.

Nutomic and I originally made Lemmy to fill the role as a federated alternative to Reddit, but as it grows, it has the potential become a main source of news and discussion, existing outside of the US’s jurisdictional domain and control.

Administration info

Information for Lemmy instance admins, and those who want to run a server.

If you have any problems in the installation, you can ask for help in !lemmy_support. Do not use Github for support.


Official/Supported methods

Lemmy has two primary installation methods:

We recommend using Ansible, because it simplifies the installation and also makes updating easier.

Lemmy uses roughly 150 MB of RAM in the default Docker installation. CPU usage is negligible.

Other installation methods

⚠️ Under your own risk.

In some cases, it might be necessary to use different installation methods.

You could use any other reverse proxy

An Example Caddy configuration.

Lemmy components


Lemmy-ui is the main frontend for Lemmy. It consists of an expressjs based server-side process (necessary for SSR) and client code which run in the browser. It does not use a lot of resources and will happily run on quite low powered servers.


Lemmy_server is the backend process, which handles:

  • Incoming HTTP requests (both from Lemmy clients and incoming federation from other servers)
  • Outgoing federation
  • Scheduled tasks (most notably, constant hot rank calculations, which keep the front page fresh)


Pict-rs is a service which does image processing. It handles user-uploaded images as well as downloading thumbnails for external images.

Docker Installation

Make sure you have both docker and docker-compose(>=1.24.0) installed. On Ubuntu, just run apt install docker-compose Next,

# Create a folder for the lemmy files. the location doesnt matter, you can put this anywhere you want
mkdir lemmy
cd lemmy

Download default config files

These files contain {{ }} braces for variables, such as passwords and your domain.

Edit them before starting up lemmy for the first time.

The images will likely be: dessalines/lemmy:VERSION and dessalines/lemmy-ui:VERSION

wget -O lemmy.hjson

If you'd like further customization, have a look at the config file named lemmy.hjson, and adjust it accordingly.

Database tweaks

To optimize your database, add this file.

You can input your system specs, using this tool:


Folder permissions

Set the correct permissions for pictrs folder:

mkdir -p volumes/pictrs
sudo chown -R 991:991 volumes/pictrs

Finally, run:

docker-compose up -d

lemmy-ui is accessible on the server at http://localhost:{{ lemmy_port }}

Reverse Proxy / Webserver

Here's an optional nginx reverse proxy template, which you can place in /etc/nginx/sites-enabled

Alternatively, you can use any other web server such as caddy as a simple reverse proxy.

Be sure to edit the {{ }} to match your domain and port.

If you're using this, you will need to set up Let's Encrypt. See those instructions below.


If you've set up Let's Encrypt and your reverse proxy, you can go to https://{{ domain }}

Let's Encrypt

You should also setup TLS, for example with Let's Encrypt. Here's a guide for setting up letsencrypt on Ubuntu.

For federation to work, it is important that you do not change any headers that form part of the signature. This includes the Host header - you may need to refer to the documentation for your proxy server to pass through the Host header unmodified.


To update to the newest version, you can manually change the version in docker-compose.yml. Alternatively, fetch the latest version from our lemmy-ansible repo:

# Then replace the {{ }} vars again
docker-compose up -d

Ansible Installation

Follow the instructions on the Lemmy-Ansible repo.

From Scratch

These instructions are written for Ubuntu 20.04 / Ubuntu 22.04.


Lemmy Backend

It is built from source, so this may take a while, especially on slow devices. For example, Lemmy v0.12.2 takes 17 minutes to build on a dual core VPS. If you prefer prebuilt binaries, use Docker or Ansible install methods.

For the Rust compiles, it is ideal to use a non-privledged Linux account on your system.

Install Rust by following the instructions on Rustup (using a non-privledged Linux account, it will install file in that user's home folder for rustup and cargo).

Lemmy supports image hosting using pict-rs. We need to install a couple of dependencies for this. You can also skip these steps if you don't require image hosting. NOTE: Lemmy-ui will still allow users to attempt uploading images even if pict-rs is not configured, in this situation, the upload will fail and users will receive technical error messages.

Depending on preference, pict-rs can be installed as a standalone application, or it can be embedded within Lemmy itself (see below). In both cases, pict-rs requires the magick command which comes with Imagemagick version 7, but Ubuntu 20.04 only comes with Imagemagick 6. So you need to install that command manually, eg from the official website.

sudo apt install ffmpeg exiftool libgexiv2-dev --no-install-recommends
# save the file to a working folder it can be verified before copying to /usr/bin/
# compare hash with the "message digest" on the official page linked above
sha256sum magick
sudo mv magick /usr/bin/
sudo chmod 755 /usr/bin/magick

Install dependencies and setup database:

sudo apt install pkg-config libssl-dev libpq-dev postgresql

# replace db-passwd with a unique password of your choice
sudo -iu postgres psql -c "CREATE USER lemmy WITH PASSWORD 'db-passwd';"
sudo -iu postgres psql -c "CREATE DATABASE lemmy WITH OWNER lemmy;"
# NOTE: this may be required by migration, depending on version of Lemmy
#   sudo -iu postgres psql -c "ALTER USER lemmy WITH SUPERUSER;"
# create user account on Linux for the lemmy_server application
sudo adduser lemmy --system --disabled-login --no-create-home --group

Tune your PostgreSQL settings to match your hardware via

Compile and install Lemmy, given the from-scratch intention, this will be done via GitHub checkout. This can be done by a normal unprivledged user (using the same Linux account you used for rustup).

# protobuf-compiler may be required for Ubuntu 22.04.2 installs, please report testing in lemmy-docs issues
sudo apt install protobuf-compiler
git clone lemmy
cd lemmy
git checkout 0.18.2
git submodule init
git submodule update --recursive --remote
echo "pub const VERSION: &str = \"$(git describe --tag)\";" > "crates/utils/src/"
# These instructions assume you build pictrs independent, but it is
# OPTIONAL on next command: --features embed-pictrs
cargo build --release
# copy compiled binary to destination
sudo cp target/release/lemmy_server /usr/bin/lemmy_server


  • Lemmy currently only supports non-SSL connections to databases. More info here.
  • Your postgres config might need to be edited to allow password authentication instead of peer authentication. Simply add:
    local   lemmy           lemmy                                   md5
    # Add the line above.
    # Do not add the line below, it should already exist in your pg_hba.conf in some form.
    local   all             all                                     peer

Minimal Lemmy config, put this in /etc/lemmy/lemmy.hjson (see here for more config options). Run chown lemmy:lemmy /etc/lemmy/ -R to set the correct owner.

  database: {
    # put your db-passwd from above
    password: "db-passwd"
  # replace with your domain
  bind: ""
  federation: {
    enabled: true
  # remove this block if you don't require image hosting
  pictrs: {
    url: "http://localhost:8080/"

Systemd unit file, so that Lemmy automatically starts and stops, logs are handled via journalctl etc. Put this file into /etc/systemd/system/lemmy.service, then run systemctl enable lemmy and systemctl start lemmy.

Description=Lemmy - A link aggregator for the fediverse

# remove these two lines if you don't need pict-rs

# Hardening


If you did everything right, the Lemmy logs from journalctl -u lemmy should show "Starting http server at". You can also run curl localhost:8536/api/{version}/site which should give a successful response, looking like {"site_view":null,"admins":[],"banned":[],"online":0,"version":"unknown version","my_user":null,"federated_instances":null}. For pict-rs, run curl and ensure that it outputs nothing (particularly no errors).

Install lemmy-ui (web frontend)

Install dependencies (nodejs and yarn in Ubuntu 20.04 / Ubuntu 22.04 repos are too old)

curl -sS | sudo apt-key add -
echo "deb stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
curl -fsSL | sudo -E bash -
sudo apt install nodejs yarn

Clone the git repo, checkout the version you want (0.18.2 in this case), and compile it.

mkdir /var/lib/lemmy-ui
cd /var/lib/lemmy-ui
chown lemmy:lemmy .
# dont compile as admin
sudo -u lemmy bash
git clone --recursive .
git checkout 0.18.2 # replace with the version you want to install
yarn install --pure-lockfile
yarn build:prod

Add another systemd unit file, this time for lemmy-ui. You need to replace with your actual domain. Put the file in /etc/systemd/system/lemmy-ui.service, then run systemctl enable lemmy-ui and systemctl start lemmy-ui. More UI-related variables can be found here.

Description=Lemmy UI - Web frontend for Lemmy

ExecStart=/usr/bin/node dist/js/server.js

# Hardening


If everything went right, the command curl -I localhost:1234 should show 200 OK at the top.

Configure reverse proxy and TLS

Install dependencies

apt install nginx certbot python3-certbot-nginx

Request Let's Encrypt TLS certificate (just follow the instructions)

certbot certonly --nginx

Let's Encrypt certificates should be renewed automatically, so add the line below to your crontab, by running sudo crontab -e. Replace with your actual domain.

@daily certbot certonly --nginx --cert-name -d --deploy-hook 'nginx -s reload'

Finally, add the nginx config. After downloading, you need to replace some variables in the file.

curl \
    --output /etc/nginx/sites-enabled/lemmy.conf
# put your actual domain instead of
sed -i -e 's/{{domain}}/' /etc/nginx/sites-enabled/lemmy.conf
sed -i -e 's/{{lemmy_port}}/8536/g' /etc/nginx/sites-enabled/lemmy.conf
sed -i -e 's/{{lemmy_ui_port}}/1234/g' /etc/nginx/sites-enabled/lemmy.conf
nginx -s reload

Now open your Lemmy domain in the browser, and it should show you a configuration screen. Use it to create the first admin user and the default community.



Compile and install lemmy_server changes. This compile can be done by a normal unprivledged user (using the same Linux account you used for rustup and first install of Lemmy).

rustup update
cd lemmy
git checkout main
git pull --tags
git checkout 0.18.2 # replace with version you are updating to
git submodule update --recursive --remote
echo "pub const VERSION: &str = \"$(git describe --tag)\";" > "crates/utils/src/"
# These instructions assume you build pictrs independent, but it is
# OPTIONAL on next command: --features embed-pictrs
cargo build --release
# copy compiled binary to destination
# the old file will be locked by the already running application, so this sequence is recommended:
sudo -- sh -c 'systemctl stop lemmy && cp target/release/lemmy_server /usr/bin/lemmy_server && systemctl start lemmy'

Lemmy UI

cd /var/lib/lemmy-ui
sudo -u lemmy bash
git checkout main
git pull --tags
git checkout 0.18.2 # replace with the version you are updating to
git submodule update
yarn install --pure-lockfile
yarn build:prod
systemctl restart lemmy-ui


If you did not use the --features embed-pictrs flag, then this script below is necessary for installing/updating Pict-rs as a standalone server. Otherwise, pict-rs should update with lemmy_server.

rustup update
cd /var/lib/pictrs-source
git checkout main
git pull --tags
# check docker-compose.yml for pict-rs version used by lemmy
git checkout v0.2.6-r2  # replace with the version you want to install
# or simply add the bin folder to your $PATH
$HOME/.cargo/bin/cargo build --release
cp target/release/pict-rs /usr/bin/
systemctl restart pictrs

Installing on AWS

⚠️ Disclaimer: this installation method is not recommended by the Lemmy developers. If you have any problems, you need to solve them yourself or ask the respective authors. If you notice any Lemmy bugs on an instance installed like this, please mention it in the bug report.


This contains the necessary infrastructure definitions to deploy Lemmy to AWS using their Cloud Development Kit.


  • ECS Fargate cluster
    • Lemmy-UI
    • Lemmy
    • Pictrs
  • CloudFront CDN
  • EFS storage for image uploads
  • Aurora Serverless Postgres DB
  • Bastion VPC host
  • Load balancers for Lemmy
  • DNS records for your site


Clone the Lemmy-CDK.

Clone Lemmy and Lemmy-UI to the directory above this.

cp example.env.local .env.local
# edit .env.local

You should edit .env.local with your site settings.

npm install -g aws-cdk
npm install
cdk bootstrap
cdk deploy


This is not the cheapest way to run Lemmy. The Serverless Aurora DB can run you ~$90/mo if it doesn't go to sleep.

Useful CDK commands

  • npm run build compile typescript to js
  • npm run watch watch for changes and compile
  • npm run test perform the jest unit tests
  • cdk deploy deploy this stack to your default AWS account/region
  • cdk diff compare deployed stack with current state
  • cdk synth emits the synthesized CloudFormation template

Administration First Steps

After you successfully installed Lemmy either manually with Docker or automatically with Ansible here are some recommendations for a new administrator of a Lemmy server.

Admin Settings

The first thing to do is to go to your admin panel, which can be found by clicking on the cog at the top right next to the search icon. Here you can define a description for your site, so that people know if it is about one specific topic or if all subjects are welcome. You can also add an icon and a banner that define your server, it can for example be the logo of your organization.

Take the time to browse through the entire page to discover the different options you have to customize your Lemmy instance, on the same page you can edit your configuration file, where you can find information about your database, the email used by the server, the federation options or who is the main administrator.

It is always good to define another administrator than yourself, in case it is necessary to take actions while you take your best nap. Take a look at the moderation guide for more information on how to do this.

Check that everything is working properly


The easiest way to check that the email is set up correctly is to request a password renewal. You will need to set up an email in your settings if you have not already done so.

After that just log out, go to the Login page, enter your email in the Email or Username box and press forgot password. If everything is set up correctly, you should receive an email to renew your password. You can ignore this email.


Federation is disabled by default, and needs to be enabled either through the online admin panel or directly through the config.json file.

To test that your instance federation is working correctly execute curl -H 'Accept: application/activity+json', it should return json data, and not an .html file. If that is unclear to you, it should look similar to the output of curl -H 'Accept: application/activity+json'

Inclusion on instance list

To be included in the list of Lemmy instances on you must meet the following requirements:

  • Federate with at least one instance from the list
  • Have a site description and icon
  • Don't have closed sign-ups
  • Have at least 5 users who posted or commented at least once in the past month
  • Be on the latest major version of Lemmy
  • Be patient and wait the site to be updated, there's no fixed schedule for that

Recommended instances are defined in code here and the code that powers the crawler is visible here.

In the meantime you can always promote your server on other social networks like Mastodon using the hashtag #Lemmy.

Keeping up to date

You can subscribe to the Github RSS feeds to be informed of the latest releases:

There is also a Matrix chat for instance administrators that you can join. You'll find some really friendly people there who will help you (or at least try to) if you run into any issue.


The configuration is based on the file config.hjson, which is located by default at config/config.hjson. To change the default location, you can set the environment variable LEMMY_CONFIG_LOCATION.

Additional environment variable are available:

  • LEMMY_DATABASE_URL, which can be used with a PostgreSQL connection string like postgres://lemmy:password@lemmy_db:5432/lemmy, passing all connection details at once,
  • LEMMY_SMTP_PASSWORD, which can be used to set the password to authenticate with the SMTP server.

Those environment variables will override values if specified in the config file.

If the Docker container is not used, manually create the database specified above by running the following commands:

cd server

Full config with default values

{{#include ../../include/config/defaults.hjson}}

Lemmy-UI configuration

Lemmy-UI can be configured using environment variables, detailed in its README.

Theming Guide

Lemmy uses Bootstrap v4, and very few custom css classes, so any bootstrap v4 compatible theme should work fine. Use a tool like to create a bootstrap v4 theme. Export the bootstrap.min.css once you're done, and save the _variables.scss too.

If you installed Lemmy with Docker, save your theme file to ./volumes/lemmy-ui/extra_themes. For native installation (without Docker), themes are loaded by lemmy-ui from ./extra_themes folder. A different path can be specified with LEMMY_UI_EXTRA_THEMES_FOLDER environment variable.

After a theme is added, users can select it under /settings. Admins can set a theme as site default under /admin.


Lemmy has three types of federation:

  • Allowlist: Explicitly list instances to connect to.
  • BlockList: Explicitly list instances to not connect to. Federation is open to all other instances.
  • Open: Federate with all potential instances.

Federation is enabled by default. You can add allowed and blocked instances, by adding a comma-delimited list in your instance admin panel. IE to only federate with these instances, add:, to the allowed instances section.

Lemmy uses the ActivityPub protocol (a W3C standard) to enable federation between different servers (often called instances). This is very similar to the way email works. For example, if you use, then you can not only send mails to other users, but also to, and so on. Email uses the SMTP protocol to achieve this, so you can think of ActivityPub as "SMTP for social media". The amount of different actions possible on social media (post, comment, like, share, etc) means that ActivityPub is much more complicated than SMTP.

As with email, ActivityPub federation happens only between servers. So if you are registered on, you only connect to the API of, while the server takes care of sending and receiving data from other instances (eg The great advantage of this approach is that the average user doesn't have to do anything to use federation. In fact if you are using Lemmy, you are likely already using it. One way to confirm is by going to a community or user profile. If you are on and you see a user like, or a community like !, then those are federated, meaning they use a different instance from yours.

One way you can take advantage of federation is by opening a different instance, like, and browsing it. If you see an interesting community, post or user that you want to interact with, just copy its URL and paste it into the search of your own instance. Your instance will connect to the other one (assuming the allowlist/blocklist allows it), and directly display the remote content to you, so that you can follow a community or comment on a post. Here are some examples of working searches:

  • ! (Community)
  • (User)
  • (Community)
  • (User)
  • (Post)
  • (Comment)

You can see the list of linked instances by following the "Instances" link at the bottom of any Lemmy page.

Fetching communities

If you search for a community first time, 20 posts are fetched initially. Only if a least one user on your instance subscribes to the remote community, will the community send updates to your instance. Updates include:

  • New posts, comments
  • Votes
  • Post, comment edits and deletions
  • Mod actions

You can copy the URL of the community from the address bar in your browser and insert it in your search field. Wait a few seconds, the post will appear below. At the moment there is no loading indicator for the search, so wait a few seconds if it shows "no results".

Fetching posts

Paste the URL of a post into your Lemmy instance's search field. Wait a few seconds until the post appears. This will also fetch the community profile, and the profile of the post creator.

Fetching comments

If you find an interesting comment under a posting on another instance, you can find below the comment in the 3-dot-menu the link-symbol. Copy this link. It looks like Remove the post/XXX part and put it into your search-bar. For this example, search for This comment, all parent comments, users and community and the corresponding post are fetched from the remote instance, if they are not known locally.

Sibling comments are not fetched! If you want more comments from older posts, you have to search for each of them as described above.


Different problems that can occur on a new instance, and how to solve them.

Many Lemmy features depend on a correct reverse proxy configuration. Make sure yours is equivalent to our nginx config.



For frontend issues, check the browser console for any error messages.

For server logs, run docker-compose logs -f lemmy in your installation folder. You can also do docker-compose logs -f lemmy lemmy-ui pictrs to get logs from different services.

If that doesn't give enough info, try changing the line RUST_LOG=error in docker-compose.yml to RUST_LOG=info or RUST_LOG=verbose, then do docker-compose restart lemmy.

Creating admin user doesn't work

Make sure that websocket is working correctly, by checking the browser console for errors. In nginx, the following headers are important for this:

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

Rate limit error when many users access the site

Check that the headers X-Real-IP and X-Forwarded-For are sent to Lemmy by the reverse proxy. Otherwise, it will count all actions towards the rate limit of the reverse proxy's IP. In nginx it should look like this:

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;


Other instances can't fetch local objects (community, post, etc)

Your reverse proxy (eg nginx) needs to forward requests with header Accept: application/activity+json to the backend. This is handled by the following lines:

set $proxpass "{{ lemmy_ui_port }}";
if ($http_accept = "application/activity+json") {
set $proxpass "{{ lemmy_port }}";
if ($http_accept = "application/ld+json; profile=\"\"") {
set $proxpass "{{ lemmy_port }}";
proxy_pass $proxpass;

You can test that it works correctly by running the following commands, all of them should return valid JSON:

curl -H "Accept: application/activity+json"
curl -H "Accept: application/activity+json"
curl -H "Accept: application/activity+json" # the id of a local post
curl -H "Accept: application/activity+json" # the id of a local comment

Fetching remote objects works, but posting/commenting in remote communities fails

Check that federation is allowed on both instances.

Also ensure that the time is accurately set on your server. Activities are signed with a timestamp, and will be discarded if it is off by more than 10 seconds.

Other instances don't receive actions reliably

Lemmy uses a queue to send out activities. The size of this queue is specified by the config value federation.worker_count. Very large instances might need to increase this value. Search the logs for "Activity queue stats", if it is consistently larger than the worker_count (default: 64), the count needs to be increased.

Backup and Restore Guide

Docker and Ansible

When using docker or ansible, there should be a volumes folder, which contains both the database, and all the pictures. Copy this folder to the new instance to restore your data.

Full Database backup

To take a complete backup of the DB to a .sql.gz file, you can run:

docker-compose exec postgres pg_dumpall -c -U lemmy | gzip > lemmy_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.gz

For compression, you can use either gzip and gunzip, or xz and unxz.

A Sample backup script

# DB Backup
ssh MY_USER@MY_IP "docker-compose exec postgres pg_dumpall -c -U lemmy" | gzip > ~/BACKUP_LOCATION/INSTANCE_NAME_dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.gz

# Volumes folder Backup
rsync -avP -zz --rsync-path="sudo rsync" MY_USER@MY_IP:/LEMMY_LOCATION/volumes ~/BACKUP_LOCATION/FOLDERNAME

Restoring the DB

To restore, run:

docker-compose up -d postgres

# Restore from the .sql.gz backup
gunzip < db_dump.sql  |  docker-compose exec -T postgres psql -U lemmy

# Note: You may need to change the permissions on the postgres directory, depending on your system.
chown -R $USER volumes
docker-compose restart postgres

# Continue with the startup
docker-compose up -d

If you've accidentally already started the lemmy service, you need to clear out your existing database:

# Drop the existing DB
docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"

# This also might be necessary when doing a db import with a different password.
docker exec -i FOLDERNAME_postgres_1 psql -U lemmy -c "alter user lemmy with password 'bleh'"

Then run the restore commands above.

Changing your domain name

If you haven't federated yet, you can change your domain name in the DB. Warning: do not do this after you've federated, or it will break federation.

Get into psql for your docker:

docker-compose exec postgres psql -U lemmy

-- Post
update post set ap_id = replace (ap_id, 'old_domain', 'new_domain');
update post set url = replace (url, 'old_domain', 'new_domain');
update post set body = replace (body, 'old_domain', 'new_domain');
update post set thumbnail_url = replace (thumbnail_url, 'old_domain', 'new_domain');

-- Comments
update comment set ap_id = replace (ap_id, 'old_domain', 'new_domain');
update comment set content = replace (content, 'old_domain', 'new_domain');

-- User
update user_ set actor_id = replace (actor_id, 'old_domain', 'new_domain');
update user_ set inbox_url = replace (inbox_url, 'old_domain', 'new_domain');
update user_ set shared_inbox_url = replace (shared_inbox_url, 'old_domain', 'new_domain');
update user_ set avatar = replace (avatar, 'old_domain', 'new_domain');

-- Community
update community set actor_id = replace (actor_id, 'old_domain', 'new_domain');
update community set followers_url = replace (followers_url, 'old_domain', 'new_domain');
update community set inbox_url = replace (inbox_url, 'old_domain', 'new_domain');
update community set shared_inbox_url = replace (shared_inbox_url, 'old_domain', 'new_domain');

More resources


Using Caddy as reverse proxy example

If you prefer to use Caddy instead of Nginx - you could use this template to fit into your needs:

(caddy-common) {
    encode gzip
    header {
        Strict-Transport-Security "max-age=31536000; include-subdomains;"
        X-XSS-Protection "1; mode=block"
        X-Frame-Options "DENY"
        X-Content-Type-Options nosniff
        Referrer-Policy  no-referrer-when-downgrade
        X-Robots-Tag "none"
} {
        import caddy-common
        reverse_proxy   http://lemmy_lemmy-ui_1:1234

@lemmy {
        path    /api/*
        path    /pictrs/*
        path    /feeds/*
        path    /nodeinfo/*
        path    /.well-known/*

@lemmy-hdr {
        header Accept application/*

handle @lemmy {
        reverse_proxy   http://lemmy_lemmy_1:8536

handle @lemmy-hdr {
        reverse_proxy   http://lemmy_lemmy_1:8536

@lemmy-post {
        method POST

handle @lemmy-post {
        reverse_proxy   http://lemmy_lemmy_1:8536

Tor Hidden Services

This guide assumes Lemmy has been installed using the official Docker Compose method.

Note that federation is not currently supported over the Tor network. An existing Lemmy instance is required. This procedure will proxy Lemmy though Tor, but federation tasks are still handled by HTTPS on the open internet.

Tor ("The Onion Router") is software designed to circumvent censorship and prevent bad actors from monitoring your activity on the internet by encrypting and distributing network traffic through a decentralized pool of relay servers run by volunteers all over the world.

A Tor hidden service is only accessible through the Tor network using the .onion top-level domain with the official Tor Browser, or any client capable of communicating over a SOCKS5 proxy. Hosting a service on the Tor network is a good way to promote digital privacy and internet freedom.

Installing Tor

The official documentation suggests Ubuntu and Debian users install Tor from the repository because it always provides the latest stable release of the software.

Administrative Access is Required

Commands below are expected to be executed as the root user. To become root use sudo -i or su -.

With sudo:

sudo -i
Password: [authenticate with current user password]

With su:

su -
Password: [authenticate with root's password]

Note: To return to your account run: exit.

Verify your architecture is supported

The package repository only supports amd64, arm64, and i386 architectures.

dpkg --print-architecture

If your architecture is not supported you may want to consider installing Tor from source.

Install prerequisite packages

apt install -y apt-transport-https ca-certificates gpg lsb-release wget

Enable the repository

Configure apt to pull packages from

bash -c 'dist=$(lsb_release -s -c); /bin/echo -e \
"deb [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] \ $dist main\n\
deb-src [signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] \ $dist main" \
> /etc/apt/sources.list.d/tor.list'

Import's GPG signing key

wget -qO- \
    | gpg --dearmor \
    | tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null

The signing key ensures the package retrieved from the server was created by

Install tor

apt update && apt install -y tor

Creating a Tor hidden service

Create a new hidden service directory:

mkdir /var/lib/tor/hidden_lemmy_service

Append the following to /etc/tor/torrc to tie the hidden service directory to the tor daemon:

HiddenServiceDir /var/lib/tor/hidden_lemmy_service/
HiddenServicePort 80

HiddenServiceDir [path] is where tor will store data related to the hidden service, and HiddenServicePort [hidden_service_port] [host_ip:port] binds a port on the host to a hidden service port on the Tor network.

Enable and start the Tor daemon

systemctl enable --now tor

At startup tor daemon will automatically populate /var/lib/tor/hidden_lemmy_service/ with encryption keys, certificates, and assign a hostname for the new service.

Determine your hidden service's hostname

cat /var/lib/tor/hidden_lemmy_service/hostname

The .onion address contained in this file will be referred to as HIDDEN_SERVICE_ADDR from here on.

Configure your existing Lemmy instance

Docker compose

Forward port 10080 from the proxy container to the hidden service port This exposes 10080/tcp to the local host, and will not be directly accessible from the internet. For context "80:80" binds port 80/tcp (HTTP) to on the host. Unless a firewall is configured to block incoming traffic to 80 this will be exposed to other hosts on the local area network (LAN) and/or the open internet.


  # ...
    # ...
      - "80:80"
      - "443:443"
      - ""

Configure NGINX

Append a new server {...} block to handle tor traffic, and add the Onion-Location header to the SSL encrypted server exposed to the internet. This header informs Tor Browser users that an equivalent .onion site exists on the Tor network by displaying an icon next to the address bar.


worker_processes 1;
events {
    worker_connections 1024;

http {
    # Original configuration listening on port 80
    server {
        listen 80;
        # ...

    # Original configuration listening on port 443
    server {
        listen 443;
        # ...
        location / {
            # Handle Tor Browser's ".onion" link detection
            add_header Onion-Location "http://HIDDEN_SERVICE_ADDR$request_uri" always;
            # ...

    # Establish a rate limit for the hidden service address
    limit_req_zone $binary_remote_addr zone=HIDDEN_SERVICE_ADDR_ratelimit:10m rate=1r/s;

    # Add tor-specific upstream aliases as a visual aid to
    # avoid editing the incorrect server block in the future
    upstream lemmy-tor {
        server "lemmy:8536";
    upstream lemmy-ui-tor {
        server "lemmy-ui:1234";

    # Add a copy of your current internet-facing configuration with
    # "listen" and "server_listen" modified to send all traffic
    # over the Tor network, incorporating the visual upstream aliases
    # above.
    server {
        # Tell nginx to listen on the hidden service port
        listen 10080;

        # Set server_name to the contents of the file:
        # /var/lib/tor/hidden_lemmy_service/hostname
        server_name HIDDEN_SERVICE_ADDR;

        # Hide nginx version
        server_tokens off;

        # 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";

        # Upload limit for pictrs
        client_max_body_size 20M;

        # frontend
        location / {
            # distinguish between ui requests and backend
            # don't change lemmy-ui or lemmy here, they refer to the upstream definitions on top
            set $proxpass "http://lemmy-ui-tor";

            if ($http_accept = "application/activity+json") {
                set $proxpass "http://lemmy-tor";
            if ($http_accept = "application/ld+json; profile=\"\"") {
                set $proxpass "http://lemmy-tor";
            if ($request_method = POST) {
                set $proxpass "http://lemmy-tor";
            proxy_pass $proxpass;

            rewrite ^(.+)/+$ $1 permanent;

            # Send actual client IP upstream
            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
        location ~ ^/(api|feeds|nodeinfo|.well-known) {
            proxy_pass "http://lemmy-tor";
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";

            # Rate limit
            limit_req zone=HIDDEN_SERVICE_ADDR_ratelimit burst=30 nodelay;

            # Add IP forwarding headers
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # pictrs only - for adding browser cache control.
        location ~ ^/(pictrs) {
            # allow browser cache, images never update, we can apply long term cache
            expires 120d;
            add_header Pragma "public";
            add_header Cache-Control "public";

            proxy_pass "http://lemmy-tor";
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";

            # Rate limit
            limit_req zone=HIDDEN_SERVICE_ADDR_ratelimit burst=30 nodelay;

            # Add IP forwarding headers
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        # Redirect pictshare images to pictrs
        location ~ /pictshare/(.*)$ {
            return 301 /pictrs/image/$1;

    # ...

Apply the configuration(s)

Restart all services associated with your Lemmy instance:

docker compose down
docker compose up -d

Test connectivity over Tor

Using torsocks, verify your hidden service is available on the Tor network.

torsocks curl -vI http://HIDDEN_SERVICE_ADDR
*   Trying 127.*.*.*:80...
* Connected to HIDDEN_SERVICE_ADDR (127.*.*.*) port 80 (#0)
> HEAD / HTTP/1.1
> User-Agent: curl/7.76.1
> Accept: */*
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: nginx
Server: nginx
< Date: Wed, 07 Jun 2023 17:06:00 GMT
Date: Wed, 07 Jun 2023 17:06:00 GMT
< Content-Type: text/html; charset=utf-8
Content-Type: text/html; charset=utf-8
< Content-Length: 98487
Content-Length: 98487
< Connection: keep-alive
Connection: keep-alive
< Vary: Accept-Encoding
Vary: Accept-Encoding
< X-Powered-By: Express
X-Powered-By: Express
< Content-Security-Policy: default-src 'self'; manifest-src *; connect-src *; img-src * data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; form-action 'self'; base-uri 'self'; frame-src *
Content-Security-Policy: default-src 'self'; manifest-src *; connect-src *; img-src * data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; form-action 'self'; base-uri 'self'; frame-src *
< ETag: W/"180b7-EC9iFYAIlbnN8zHCayBwL3wAm64"
ETag: W/"180b7-EC9iFYAIlbnN8zHCayBwL3wAm64"
< Referrer-Policy: same-origin
Referrer-Policy: same-origin
< X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
X-Frame-Options: DENY
< X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; mode=block

* Connection #0 to host HIDDEN_SERVICE_ADDR left intact

Logging behavior

Hidden service traffic will appear to originate from the lemmyexternalproxy docker network instead of an internet IP. Docker's default network address pool is

docker compose logs -f proxy
lemmy-proxy-1  | 172.*.0.1 - -  # ...
lemmy-proxy-1  | 172.*.0.1 - -  # ...
lemmy-proxy-1  | 172.*.0.1 - -  # ...


Lemmy supports the export of metrics in the Prometheus format. This document outlines how to enable the feature and begin collecting metrics.


A cargo feature flag gates the inclusion of the Prometheus components in the Lemmy server. To build Lemmy with the Prometheus components included, enable the prometheus-metrics feature.

cargo build --features prometheus-metrics

If using the docker-compose.yml from the Lemmy repository, enable the feature flag through the CARGO_BUILD_FEATURES build arg.

      CARGO_BUILD_FEATURES: prometheus-metrics


Configuration of the Prometheus endpoint is contained under the optional prometheus object in the lemmy.hjson file. In this configuration block, the bind address and port of the Prometheus metrics server can be configured.

By default, it will serve on If running inside a container, it should instead bind on all addresses as below.

prometheus: {
  bind: ""
  port: 10002


After Lemmy has been built and deployed, test that it is serving properly (substitute the correct hostname if not running locally).

curl localhost:10002/metrics

Prometheus Configuration

Below is a minimal configuration on the Prometheus server's side that will scrape the metrics from Lemmy.

  - job_name: lemmy
    scrape_interval: 1m
      - targets:
          - localhost:10002

Then run a Prometheus server to scrape the metrics.

docker run -p 9090:9090 -v prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus

If Lemmy was deployed using the docker-compose.yml, then use the following configurations instead.

  - job_name: lemmy
    scrape_interval: 1m
      - targets:
          - lemmy:10002

Run the Prometheus server inside the same docker network that Lemmy is running in. This can be discovered by running docker inspect on the lemmy container.

docker run -p 9090:9090 -v prometheus.yml:/etc/prometheus/prometheus.yml --network $LEMMY_NETWORK prom/prometheus


Below is an example of what is returned from the /metrics endpoint. Each combination of endpoint, method, and status will produce a histogram (lemmy_api_http_requests_duration_seconds_*), so for brevity the output was reduced to a single endpoint.

For the HTTP metrics, this is saying that /api/v3/post/list received 12 GET requests that returned a 200 HTTP code. Cumulatively, these requests took 0.383 seconds. 5 requests completed between 0.01 and 0.025 seconds. 5 more completed between 0.025 and 0.05 seconds. And the remaining 2 requests completed between 0.05 and 0.1 seconds.

lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.005"} 0
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.01"} 0
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.025"} 5
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.05"} 10
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.1"} 12
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.25"} 12
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="0.5"} 12
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="1"} 12
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="2.5"} 12
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="5"} 12
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="10"} 12
lemmy_api_http_requests_duration_seconds_bucket{endpoint="/api/v3/post/list",method="GET",status="200",le="+Inf"} 12
lemmy_api_http_requests_duration_seconds_sum{endpoint="/api/v3/post/list",method="GET",status="200"} 0.3834808429999999
lemmy_api_http_requests_duration_seconds_count{endpoint="/api/v3/post/list",method="GET",status="200"} 12

lemmy_api_http_requests_total{endpoint="/api/v3/post/list",method="GET",status="200"} 12

# HELP lemmy_db_pool_available_connections Number of available connections in the pool
# TYPE lemmy_db_pool_available_connections gauge
lemmy_db_pool_available_connections 2
# HELP lemmy_db_pool_connections Current number of connections in the pool
# TYPE lemmy_db_pool_connections gauge
lemmy_db_pool_connections 2
# HELP lemmy_db_pool_max_connections Maximum number of connections in the pool
# TYPE lemmy_db_pool_max_connections gauge
lemmy_db_pool_max_connections 5

# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 14
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 1073741816
# HELP process_open_fds Number of open file descriptors.
# TYPE process_open_fds gauge
process_open_fds 91
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 75603968
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1688487611
# HELP process_threads Number of OS threads in the process.
# TYPE process_threads gauge
process_threads 37
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 206000128

Scaling Lemmy horizontally

This is a collection of notes on scaling different Lemmy components horizontally. This is not meant as a step-by-step guide, rather as general tips and knowledge that might be useful to somebody who is already familiar with and horizontal scaling concepts and best practices. If you don't already know about concepts like load balancing, object storage, and reverse proxies, then this document will probably be hard (or impossible) to understand.

Note: scaling Lemmy this way is not necessary, and for small instances, the added overhead most likely outweighs any benefits.

Why scale horizontally?

  • If one of your Lemmy servers dies for any reason, your instance can still remain operational
  • Having multiple Lemmy servers allows you to do rolling upgrades (no downtime needed to upgrade Lemmy!)
  • Horizontal scaling provides additional flexibility in how you upgrade your infrastructure
  • As of Lemmy version 0.18.2, Lemmy appears to be able to use the same resources more efficiently if they are split between multiple Lemmy processes

Breakdown of Lemmy components

See Lemmy components for a high level overview of what each component does.


Lemmy-ui can be horizontally scaled, but make sure you read the following section for information about rolling upgrades.

Rolling upgrades

In general, there is nothing preventing you from running multiple load balanced Lemmy-ui servers in parallel, but without some custom configuration, rolling upgrades will break Lemmy for your users! This is because by default, Lemmy-ui will serve static files through the express process.

Consider the scenario where you have 2 Lemmy-ui servers, one is running on version 1, and the other has just come online running version 2. A user opening your Lemmy in their browser might have their front page html request routed to version 1. This html will contain a reference to many other static files which are specific to version 1. Most likely, some of these requests will get routed to the server running version 2. The files will not exist on this server, the server will respond with 404, and the UI will remain in a broken state.

There are a few ways to work around this issue:

  1. The safest option is to ensure that all servers are able to serve static files for all currently active Lemmy-ui versions.

    • There are many ways to achieve this, the most obvious one being to upload your static files to object storage, and have reverse proxies route static file requests to your static files bucket.
    • Here's one possible example of how to do this with nginx:
    location /static {
       expires 1y;
       add_header Cache-Control "public";
       proxy_pass https://${bucket_fqdn};
       limit_except GET {
           deny  all;
       proxy_intercept_errors on;
       # invalid keys result in 403
       error_page 403 =404 /@404static;

    Replace ${bucket_fqdn} with the URL to your bucket. With this setup, you should upload the contents of the lemmy-ui dist/ directory to a "directory" named static/$LEMMY_UI_HASH in your bucket, where the hash is calculated from the commit you are deploying: export LEMMY_UI_HASH=$(git rev-parse --short HEAD). Note that this setup requires public acl on uploaded files, so it's recommended to limit access to your bucket with an IP allowlist policy.

  2. An alternative option is to configure sticky sessions in your load balancer. This will ensure that subsequent requests all end up on the same Lemmy-ui server, and as long as your sticky sessions last longer than your upgrade process, most clients should remain functional.

  3. Similarly to sticky sessions, another possibility is to configure ip hash based load balancing.

  4. There is always the option to just employ downtime and upgrade all servers at once (but that's not very fun!)


Lemmy_server can be horizontally scaled, with a few caveats.

Scheduled tasks

By default, a Lemmy_server process will always run background scheduled tasks, which are intended to be run only on one server. Launching multiple processes with the default configuration will result in multiple duplicated scheduled tasks all starting at the same moment and trying to do the same thing at once. At best, it will be a waste of resources, but it could potentially end up causing some weird glitches as well.

To solve this, Lemmy can be started with the --disable-scheduled-tasks flag on all but one instance. In general, there are two approaches:

  1. Run all your load balanced Lemmy servers with the --disable-scheduled-tasks flag, and run one additional Lemmy server without this flag which is not in your load balancer and does not accept any HTTP traffic.
  2. Run one load balanced Lemmy server without the flag, and all other load balanced servers with the flag.

Option 1 might have a few tiny advantages (easier to isolate logs for the scheduled tasks, and expensive scheduled tasks won't compete with HTTP requests for system resources), but requires an extra process.

Rolling upgrades

For most versions, rolling releases have been completely OK, but there have been some cases where a database migration slips in which is NOT backwards compatible (causing any servers running the older version of Lemmy to potentially start generating errors). To be safe, you should always first take a look at what database migrations are included in a new version, and evaluate whether the changes are for sure safe to apply with running servers.

Note that even if there are no backwards incompatible migrations between immediate version upgrades (version 1 -> 2 is safe and 2 -> 3 is safe), doing bigger upgrades might cause these same migrations to become backwards incompatible (a server running version 1 might not be compatible with migrations from version 3 in this scenario). It is generally best to only upgrade one version at a time!.

For the scheduled task process, it shouldn't cause any major issues to have two servers running in parallel for a short duration during an upgrade, but shutting the old one down before starting the new one will always be safer.


Pict-rs does not scale horizontally as of writing this document. This is due to a dependency on a Sled database, which is a disk based database file.

The developer of pict-rs has mentioned plans to eventually add Postgres support (which should in theory enable horizontal scaling), but work on this has not yet started.

Note that by caching pict-rs images (for example, with nginx, or with a CDN), you can really minimize load on the pict-rs server!

Other tips

  1. Your lemmy-ui servers need to access your backend as well. If you don't want a complicated setup with multiple load balancers, you could just have each of your servers contain nginx, lemmy-ui, and lemmy_server, and just load balance to an nginx on each of your servers

  2. Make sure you're passing the real client ip address to Lemmy, otherwise Lemmy's built in rate limiter will trigger very often (if it rate limits based on your load balancer's IP address, for example). With nginx, you should use something like this in your nginx.conf:

    real_ip_recursive on;
    real_ip_header X-Forwarded-For;
    set_real_ip_from <your load balancer ip here>;
  3. The internal Lemmy load balancer works based on an in-memory storage of ip addresses. That means that by adding more Lemmy servers, you are effectively making your rate limits less strict. If you rely on the built in limiter, make sure to adjust the limits accordingly!


Lemmy is an open source project and relies on community contributions to get better. Development happens mainly in the Lemmy Github repositories. Communication is done over Matrix.

These are the main repositories which are relevant for contributors:

  • lemmy: The backend which is the core of Lemmy. It implements SQL queries, provides the API and handles ActivityPub federation. Additionally it sends emails and provides RSS feeds. Written in Rust with actix-web and diesel. The issue tracker is also used for general enhancements which affect multiple repositories.
  • lemmy-ui: The main frontend for Lemmy. It provides the user interface that you see when viewing a Lemmy instance. Written in Typescript and CSS with the Inferno framework.
  • lemmy-ansible: Automated installation method which is recommended for users without technical knowledge.
  • joinlemmy-site: Source code for the official project website Landing page for new users which includes general information and a list of instances.
  • lemmy-js-client: Client for the Lemmy API which is used by lemmy-ui. Can also be used by other projects to get started more easily.
  • activitypub-federation-rust: High-level framework for ActivityPub federation in Rust. Was originally part of the Lemmy code, but was extracted and made more generic so that other projects can use it too.

There are many different ways to contribute, depending on your skills and interests:

Reporting issues

You may notice different problems or potential improvements when using Lemmy. You can report them in the relevant repository. Make sure to first search the issue tracker to avoid duplicates. When creating the issue, fill in the template and include as much relevant information as possible.


Use Weblate to help translate Lemmy into different languages.

Programming and Design

You can open a pull request to make changes to Lemmy. In case of bigger contributions it is recommended to discuss it in an issue first. See the next sections for instructions to compile and develop the projects.


Besides contributing with your time, you can also consider making a donation to support the developers.

Local Development

Install build requirements

Install the latest Rust version using rustup.

Debian-based distro:

sudo apt install git cargo libssl-dev pkg-config libpq-dev curl
# install yarn
curl -sS | sudo apt-key add -
echo "deb stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
sudo apt update && sudo apt install yarn

Arch-based distro:

sudo pacman -S git cargo openssl pkg-config postgresql-libs curl yarn


Install Homebrew if you don't already have it installed. Then install Node and Yarn.

brew install node yarn

Setup PostgreSQL database

Debian-based distro:

sudo apt install postgresql
sudo systemctl start postgresql

Arch-based distro:

sudo pacman -S postgresql
sudo systemctl start postgresql
# If the systemctl command errors, then run the following
sudo mkdir /var/lib/postgres/data
sudo chown postgres:postgres /var/lib/postgres/data
sudo -u postgres initdb -D /var/lib/postgres/data
sudo systemctl start postgresql


brew install postgresql
brew services start postgresql
$(brew --prefix postgresql)/bin/createuser -s postgres

Either execute scripts/, or manually initialize the postgres database:

psql -c "create user lemmy with password 'password' superuser;" -U postgres
psql -c 'create database lemmy with owner lemmy;' -U postgres
export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy

Get the code

Clone frontend and backend code to local folders. Be sure to include --recursive to initialize git submodules.

git clone --recursive
git clone --recursive

Backend development

Use cargo check to find compilation errors. To start the Lemmy backend, run cargo run. It will bind to

After making changes, you need to format the code with cargo +nightly fmt --all and run the linter with ./scripts/

Frontend development

Install dependencies by running yarn. Then run yarn dev to launch lemmy-ui locally. It automatically connects to the Lemmy backend on localhost:8536. Open localhost:1234 in your browser. The project is rebuilt automatically when you change any files.

Note that this setup doesn't support image uploads. If you want to test those, you need to use the Docker development.


Run Rust unit tests:


To run federation/API tests, first add the following lines to /etc/hosts:       lemmy-alpha       lemmy-beta       lemmy-gamma       lemmy-delta       lemmy-epsilon

Then run the following script:

cd api_tests

Docker Development


Debian-based distro:

sudo apt install git docker-compose
sudo systemctl start docker
git clone --recursive

Arch-based distro:

sudo pacman -S git docker-compose
sudo systemctl start docker

Get the code with submodules:

git clone --recursive


cd docker

Then open localhost:1236 in your browser.

Building with Docker is relatively slow. To get faster builds you need to use local development instead.

Federation Development

The federation setup allows starting a few local Lemmy instances, to test federation functionality. You can start it with the following commands:

cd docker/federation

The federation test sets up 5 instances:

InstanceAdmin usernameLocationNotes
lemmy-alphalemmy_alpha127.0.0.1:8540federated with all other instances
lemmy-betalemmy_beta127.0.0.1:8550federated with all other instances
lemmy-gammalemmy_gamma127.0.0.1:8560federated with all other instances
lemmy-deltalemmy_delta127.0.0.1:8570only allows federation with lemmy-beta
lemmy-epsilonlemmy_epsilon127.0.0.1:8580uses blocklist, has lemmy-alpha blocked

You can log into each using the instance name, and lemmy as the password, IE (lemmy_alpha, lemmy).

To start federation between instances, visit one of them and search for a user, community or post, like this. Note that the Lemmy backend runs on a different port than the frontend, so you have to increment the port number from the URL bar by one.

  • http://lemmy-gamma:8561/u/lemmy-gamma
  • http://lemmy-alpha:8541/c/main
  • http://lemmy-beta:8551/post/3


Lemmy has an HTTP API for clients and frontends. See the API documentation for more information. Instead of using the API directly you can use one of the existing libraries. You can either interact with a local development instance via http://localhost:8536, or connect to a production instance. The following instances are available for testing purposes:


More clearly arranged, unofficial API documentation is available at:

Curl Examples

GET example

The current api {version} is here.

curl "{version}/community/list?sort=Hot"`

POST example

curl -i -H \
"Content-Type: application/json" \
-d '{
  "comment_id": 374,
  "score": 1,
  "auth": eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MiwiaXNzIjoidGVzdC5sZW1teS5tbCJ9.P77RX_kpz1a_geY5eCp29sl_5mAm-k27Cwnk8JcIZJk
}' \{version}/comment/like

RSS/Atom feeds

  • All - /feeds/all.xml?sort=Hot
  • Community - /feeds/c/community-name.xml?sort=Hot
  • User - /feeds/u/user-name.xml?sort=Hot


Lemmy forwards image requests to a locally running Pictrs.

GET /pictrs/image/{filename}?format={webp, jpg, ...}&thumbnail={96}

Format and thumbnail are optional.

Create (request)

Uploaded content must be valid multipart/form-data with an image array located within the images[] key.

POST /pictrs/image

Create (response)

  "files": [
      "delete_token": "{token}",
      "file": "{file}.jpg"
  "msg": "ok"


GET /pictrs/image/delete/{delete_token}/{file}

Rust API

If you want to develop a Rust application which interacts with Lemmy, you can directly pull in the relevant API structs. This uses the exact API structs used in Lemmy, but with most heavyweight dependencies disabled. API paths or HTTP client are not included, so you need to handle those aspects manually. To get started, run cargo add lemmy_api_common in your repository. You can then use the following code to make an API request:

fn main() {
use lemmy_api_common::post::{GetPosts, GetPostsResponse};
use lemmy_db_schema::{ListingType, SortType};
use ureq::Agent;

pub fn list_posts() -> GetPostsResponse {
    let params = GetPosts {
        type_: Some(ListingType::Local),
        sort: Some(SortType::New),

You can also look at the following real-world projects as examples:

Creating a Custom Frontend

The Lemmy backend and frontend are completely separate projects. This creates a lot of potential for alternative frontends which can change much of the design and user experience of Lemmy. For example, it is possible to create a frontend in the style of a traditional forum like phpBB, a question-and-answer site like Stack Overflow, a blogging platform or an image gallery. This way you don't have to write any SQL queries, federation logic, API code and so on, but can use the proven implementation from Lemmy. It is also possible to run multiple frontends for a single Lemmy instance.


The easiest way to get started is by forking one of the existing frontends. But you can also create a new frontend from scratch. In any case the principle is the same: bind to a port to serve user requests, and connect to the API of a Lemmy instance as described above to fetch data.


You can add the lemmy-translations repository to your project as a git submodule. That way you can take advantage of same translations used in the official frontend, and you will also receive new translations contributed via Weblate.

Rate limiting

Lemmy does rate limiting for many actions based on the client IP. But if you make any API calls on the server side (e.g. in the case of server-side rendering, or javascript pre-rendering), Lemmy will take the IP of the Docker container. Meaning that all requests come from the same IP, and get rate limited much earlier. To avoid this problem, you need to pass the actual client IP via Forwarded or X-Forwarded-For HTTP header.


Lemmy uses the ActivityPub protocol for communication between servers. If you are unfamiliar with the protocol, you can start by reading the resource links. This document explains how to interact with it from other projects.

In Lemmy we use some specific terms to refer to ActivityPub items. They are essentially our specific implementations of well-known ActivityPub concepts:

  • Community: Group
  • User: Person
  • Post: Page
  • Comment: Note

Almost every action in Lemmy happens inside a group. The Federation Enhancement Proposal Group Federation gives a high-level overview how this works. The generic federation logic is implemented in the activitypub-federation library. It can also be used by other Rust projects.

Sometimes you will see a notation like Create/Note. This refers to a Create activity with a Note as object.

Below are explanations and examples for all actors, objects and activities from Lemmy. These include many optional fields which you can safely ignore.


{{#include ../../include/crates/apub/assets/lemmy/context.json}}

The context is identical for all activities and objects.



An automated actor. Users can send posts or comments to it, which the community forwards to its followers in the form of Announce.

{{#include ../../include/crates/apub/assets/lemmy/objects/group.json}}
Field NameDescription
preferredUsernameName of the actor
nameTitle of the community
sensitiveTrue indicates that all posts in the community are nsfw
attributedToFirst the community creator, then all the remaining moderators
summaryText for the community sidebar, usually containing a description and rules
iconIcon, shown next to the community name
imageBanner image, shown on top of the community page
inboxActivityPub inbox URL
outboxActivityPub outbox URL, only contains up to 20 latest posts, no comments, votes or other activities
followersFollower collection URL, only contains the number of followers, no references to individual followers
endpointsContains URL of shared inbox
publishedDatetime when the community was first created
updatedDatetime when the community was last changed
publicKeyThe public key used to verify signatures from this actor


A person, interacts primarily with the community where it sends and receives posts/comments. Can also create and moderate communities, and send private messages to other users. Can be followed from other platforms.

{{#include ../../include/crates/apub/assets/lemmy/objects/person.json}}
Field NameDescription
preferredUsernameName of the actor
nameThe user's displayname
summaryUser bio
iconThe user's avatar, shown next to the username
imageThe user's banner, shown on top of the profile
inboxActivityPub inbox URL
endpointsContains URL of shared inbox
publishedDatetime when the user signed up
updatedDatetime when the user profile was last changed
publicKeyThe public key used to verify signatures from this actor


Represents a Lemmy instance, and is used to federate global data like the instance description or site bans. It can be fetched from the root path.

{{#include ../../include/crates/apub/assets/lemmy/objects/instance.json}}
Field NameDescription
nameInstance name
summaryShort description
contentLong description (sidebar)
iconInstance icon
imageInstance banner
inboxActivityPub inbox URL
endpointsContains URL of shared inbox
publishedDatetime when the instance was created
updatedDatetime when the instance metadata
publicKeyThe public key used to verify signatures from this actor



A page with title, and optional URL and text content. The attachment URL often leads to an image, in which case a thumbnail is included. Each post belongs to exactly one community. Sent out as Page, but for receiving the types Article, Note, Video and Event are also accepted.

{{#include ../../include/crates/apub/assets/lemmy/objects/page.json}}
Field NameDescription
attributedToID of the user which created this post
toID of the community where it was posted to
nameTitle of the post (mandatory)
contentBody of the post
attachmentA single website or image link
imageThumbnail for url, only present if it is an image link
commentsEnabledFalse indicates that the post is locked, and no comments can be added
sensitiveTrue marks the post as NSFW, blurs the thumbnail and hides it from users with NSFW setting disabled
stickiedTrue means that it is shown on top of the community
publishedDatetime when the post was created
updatedDatetime when the post was edited (not present if it was never edited)


A reply to a post, or reply to another comment. Contains only text (including references to other users or communities). Lemmy displays comments in a tree structure.

{{#include ../../include/crates/apub/assets/lemmy/objects/note.json}}
Field NameDescription
attributedToID of the user who created the comment
toCommunity where the comment was made
contentThe comment text
inReplyToID of the parent object. In case of a top-level comment this is the post ID, in case of a nested comment it is the parent comment ID.
publishedDatetime when the comment was created
updatedDatetime when the comment was edited (not present if it was never edited)

Private Message

A direct message from one user to another. Can not include additional users. Threading is not implemented yet, so the inReplyTo field is missing.

{{#include ../../include/crates/apub/assets/lemmy/objects/chat_message.json}}
Field NameDescription
attributedToID of the user who created this private message
toID of the recipient
contentThe text of the private message
publishedDatetime when the message was created
updatedDatetime when the message was edited (not present if it was never edited)


Community Outbox

{{#include ../../include/crates/apub/assets/lemmy/collections/group_outbox.json}}

The outbox only contains Create/Post activities for now.

Community Followers

{{#include ../../include/crates/apub/assets/lemmy/collections/group_followers.json}}

The followers collection is only used to expose the number of followers. Actor IDs are not included, to protect user privacy.

Community Moderators

List of moderators who can perform actions like removing posts or banning users.

{{#include ../../include/crates/apub/assets/lemmy/collections/group_moderators.json}}

List of posts which are stickied in the community.

{{#include ../../include/crates/apub/assets/lemmy/collections/group_featured_posts.json}}

User Outbox

Only contains totalItems count, but no actual items for privacy reasons.

{{#include ../../include/crates/apub/assets/lemmy/collections/person_outbox.json}}


User to Community


Each Community page has a "Follow" button. Clicking this triggers a Follow activity to be sent from the user to the Community inbox. The Community will automatically respond with an Accept/Follow activity to the user inbox. It will also add the user to its list of followers, and deliver any activities about Posts/Comments in the Community to the user.

{{#include ../../include/crates/apub/assets/lemmy/activities/following/follow.json}}


After following a Community, the "Follow" button is replaced by "Unfollow". Clicking this sends an Undo/Follow activity to the Community inbox. The Community removes the User from its followers list and doesn't send any activities to it anymore.

{{#include ../../include/crates/apub/assets/lemmy/activities/following/undo_follow.json}}

Create or Update Post

When a user creates a new post, it is sent to the respective community as Create/Page. Editing a previously created post sends an almost identical activity, except the type being Update.

{{#include ../../include/crates/apub/assets/lemmy/activities/create_or_update/create_page.json}}

Create or Update Comment

A reply to a post, or to another comment as Create/Note. Can contain mentions of other users. Editing a previously created post sends an almost identical activity, except the type being Update.

The origin instance also scans the Comment for any User mentions, and sends the Create/Note to those Users as well.

{{#include ../../include/crates/apub/assets/lemmy/activities/create_or_update/create_note.json}}

Like Post or Comment

An upvote for a post or comment.

{{#include ../../include/crates/apub/assets/lemmy/activities/voting/like_note.json}}

Dislike Post or Comment

A downvote for a post or comment.

{{#include ../../include/crates/apub/assets/lemmy/activities/voting/dislike_page.json}}

Undo Like or Dislike Post or Comment

Revert a vote that was previously done by the same user.

{{#include ../../include/crates/apub/assets/lemmy/activities/voting/undo_like_note.json}}

Delete Post or Comment

Mods can remove Posts and Comments from their Communities. Admins can remove any Posts or Comments on the entire site. Communities can also be removed by admins. The item is then hidden from all users.

Removals are sent to all followers of the Community, so that they also take effect there. The exception is if an admin removes an item from a Community which is hosted on a different instance. In this case, the removal only takes effect locally.

{{#include ../../include/crates/apub/assets/lemmy/activities/deletion/delete_page.json}}

Undo Delete

Post or comment deletions can be reverted by the same user.

{{#include ../../include/crates/apub/assets/lemmy/activities/deletion/undo_delete_page.json}}

Report Post, comment or private message

Reports content for rule violation, so that mods/admins can review it.

{{#include ../../include/crates/apub/assets/lemmy/activities/community/report_page.json}}

Delete User

Sent when a user deletes his own account.

{{#include ../../include/crates/apub/assets/lemmy/activities/deletion/delete_user.json}}

Community to User

Accept Follow

Automatically sent by the community in response to a Follow. At the same time, the community adds this user to its followers list.

{{#include ../../include/crates/apub/assets/lemmy/activities/following/accept.json}}


If the Community receives any Post or Comment related activity (Create, Update, Like, Dislike, Remove, Delete, Undo etc.), it will forward this to its followers. For this, an Announce is created with the Community as actor, and the received activity as object. This is sent to all followers, so they get updated in real time.

{{#include ../../include/crates/apub/assets/lemmy/activities/community/announce_create_page.json}}


These actions can only be done by instance admins or community moderators. They are sent to the community and announced by it. See for a general overview how moderation works in Lemmy. Communities can only be created on the same instance where a user is registered. After that, mods from other instances can be added with Add/User activity.

Remove Post or Comment

Removes a post or comment. The difference to delete is that remove activities have a summary field, which contains the reason for removal, as provided by the mod/admin.

{{#include ../../include/crates/apub/assets/lemmy/activities/deletion/remove_note.json}}

Block User

Blocks a user so he can't participate anymore. The scope is determined by the target field: either a community, or a whole instance. The removeData field can optionally be set to indicate that all previous posts of the user should be deleted.

{{#include ../../include/crates/apub/assets/lemmy/activities/block/block_user.json}}

Lock post

Posts can be locked so that no new comments can be created.

{{#include ../../include/crates/apub/assets/lemmy/activities/community/lock_page.json}}

Undo mod actions

All previously listed mod actions can be reverted by wrapping the original activity in Undo. Note that Lemmy regenerates the inner activity with a new ID.

{{#include ../../include/crates/apub/assets/lemmy/activities/deletion/undo_remove_note.json}}
{{#include ../../include/crates/apub/assets/lemmy/activities/block/undo_block_user.json}}

Add or remove featured post

Posts can be pinned so that they are always shown on top of the community. This is federated with the Community featured posts collection.

{{#include ../../include/crates/apub/assets/lemmy/activities/community/add_featured_post.json}}
{{#include ../../include/crates/apub/assets/lemmy/activities/community/remove_featured_post.json}}

Add or remove mod

Add a new mod to the community. Has to be sent by an existing community mod, or an admin of the community's instance.

{{#include ../../include/crates/apub/assets/lemmy/activities/community/add_mod.json}}

An existing mod can be removed in the same way.

{{#include ../../include/crates/apub/assets/lemmy/activities/community/remove_mod.json}}

User to User

Follow a user

Users from other platforms can follow Lemmy users and receive all of their posts to the inbox. Note that users who are registered on Lemmy can only follow groups, not other users.

{{#include ../../include/crates/apub/assets/pleroma/activities/follow.json}}

Create or Update Private message

User profiles have a "Send Message" button, which opens a dialog permitting to send a private message to this user. It is sent as a Create/ChatMessage to the user inbox. Private messages can only be directed at a single User. They can also be edited with Update/ChatMessage.

{{#include ../../include/crates/apub/assets/lemmy/activities/create_or_update/create_private_message.json}}

Delete Private Message

Deletes a previous private message.

{{#include ../../include/crates/apub/assets/lemmy/activities/deletion/delete_private_message.json}}

Undo Delete Private Message

Restores a previously deleted private message. The object is regenerated from scratch, as such the activity ID and other fields are different.

{{#include ../../include/crates/apub/assets/lemmy/activities/deletion/undo_delete_private_message.json}}

Resources / Libraries






ActivityPub Resources

Official Documents


Examples and Libraries

Trending / Hot / Best Sorting algorithm


  • During the day, new posts and comments should be near the top, so they can be voted on.
  • After a day or so, the time factor should go away.
  • Use a log scale, since votes tend to snowball, and so the first 10 votes are just as important as the next hundred.



Does not take the lifetime of the thread into account, giving early comments an overwhelming advantage over later ones, with the effect being even worse in small communities. New comments pool at the bottom of the thread, effectively killing off discussion and making each thread a race to comment early. This lowers the quality of conversation and rewards comments that are repetitive and spammy.

Hacker News

While far superior to Reddit's implementation for its decay of scores over time, Hacker News' ranking algorithm does not use a logarithmic scale for scores.


Counterbalances the snowballing effect of votes over time with a logarithmic scale. Negates the inherent advantage of early comments while still ensuring that votes still matter in the long-term, not nuking older popular comments.

Rank = ScaleFactor * log(Max(1, 3 + Score)) / (Time + 2)^Gravity
Scaled_Rank = Rank / log(2 + Users_Active_Month)

Score = Upvotes - Downvotes
Time = time since submission (in hours)
Gravity = Decay gravity, 1.8 is default
Users_Active_Month = The number of users in a given community who have posted / commented in the last month.
  • Lemmy uses the same Rank algorithm above, in three sorts: Active, Hot, and Scaled.
    • Active uses the post votes, and latest comment time (limited to two days).
    • Hot uses the post votes, and the post published time.
    • Scaled is similar to Hot, but gives a boost to smaller / less active communities.
  • Use Max(1, score) to make sure all comments are affected by time decay.
  • Add 3 to the score, so that everything that has less than 3 downvotes will seem new. Otherwise all new comments would stay at zero, near the bottom.
  • The sign and abs of the score are necessary for dealing with the log of negative scores.
  • A scale factor of 10k gets the rank in integer form.

A plot of rank over 24 hours, of scores of 1, 5, 10, 100, 1000, with a scale factor of 10k.


Active User counts

Lemmy also shows counts of active users for your site, and its communities. These are counted within the last day, week, month, and half year, and are cached on starting up lemmy, and every hour.

An active user is someone who has posted or commented on our instance or community within the last given time frame. For site counts, only local users are counted. For community counts, federated users are included.

Code of Conduct

  • We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
  • Please avoid using overtly sexual aliases or other nicknames that might detract from a friendly, safe and welcoming environment for all.
  • Please be kind and courteous. There’s no need to be mean or rude.
  • Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
  • Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
  • We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term “harassment” as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don’t tolerate behavior that excludes people in socially marginalized groups.
  • Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the Lemmy moderation team immediately. Whether you’re a regular contributor or a newcomer, we care about making this community a safe place for you and we’ve got your back.
  • Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.

Message the Moderation Team on Mastodon

Email The Moderation Team


These are the policies for upholding our community’s standards of conduct. If you feel that a thread needs moderation, please contact the Lemmy moderation team .

  1. Remarks that violate the Lemmy standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
  2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
  3. Moderators will first respond to such remarks with a warning, at the same time the offending content will likely be removed whenever possible.
  4. If the warning is unheeded, the user will be “kicked,” i.e., kicked out of the communication channel to cool off.
  5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
  6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
  7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, in private. Complaints about bans in-channel are not allowed.
  8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.

In the Lemmy community we strive to go the extra step to look out for each other. Don’t just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they’re off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.

And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could’ve communicated better — remember that it’s your responsibility to make others comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.

The enforcement policies listed above apply to all official Lemmy venues; including git repositories under and, the Matrix chat; and other instances under that domain. For other projects adopting the Lemmy Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.

Adapted from the Rust Code of Conduct, which is based on the Node.js Policy on Trolling as well as the Contributor Covenant v1.3.0.