The other week I had written about Pixelfed and my plans for using it. Since this is self-hosted software and I spent a fair amount of time setting it up, I wanted to write out a detailed installation guide. Hopefully, it helps someone else out there looking to create their own setup.

Starting with the basics

The basics of my setup are:

  • An Intel i3 Intel NUC machine 1
  • Running Ubuntu Linux 20.04

I have Docker and docker-compose installed.

Begin with the Pixelfed Repo

Start here:

Clone that repository locally on your machine. This article is based upon the Pixelfed v0.11.2 release.

Make a few changes:

  • Modify composer.json and composer.lock to include "beyondcode/laravel-self-diagnosis": "^1.5". This is needed because of issue #3150. Hopefully this is fixed in Pixelfed v0.11.3+!
  • Add - "./.env.docker:/var/www/.env" in docker-compose.yml for the worker service. It’s possible this is not needed, but I added it during a troubleshooting step and figure better safe than sorry.

Once those changes are complete, run docker-compose build. This will use the Dockerfile from the repository to build docker images. You can examine the Dockerfile by looking at what is specified in the docker-compose.yml file:

      context: .
      dockerfile: contrib/docker/Dockerfile.apache
    image: pixelfed

Your Docker Compose File

With the Pixelfed image built, turn your attention to the other services spec’d in the docker-compose.yml file.

First off, switch the db service to use mariadb.

    image: mariadb:latest

With v0.11.2 of Pixelfed I initially tried and failed to use MySQL. I found this Github issue #2989 which explains the problem I was seeing, and perhaps there are other problems with using MySQL.

Next, start your containers with docker-compose up -d. Give them a second to load, and check their status with docker-compose ps.

With the containers running follow Pixelfed’s install guide.

Pixelfed Install Guide Steps

A very useful reference is Pixelfed’s Installation Guide. Please note: there is a potential re-write of this guide coming sometime in 2022, as mentioned in Pixelfed’s Discord server.

From the guide, use these specific sections:

  • Configure environment variables
  • Setting up services

Environment Variables

Follow Pixelfed’s instructions to set APP_NAME, APP_URL, APP_DOMAIN, ADMIN_DOMAIN, and SESSION_DOMAIN.

These are my general settings:

## General Settings

Note that the URL is a full URL, while the domains are simply the host info.

Side note on hosting and DNS: Depending on where you are installing Pixelfed (your own homelab server, or a cloud droplet like DigitalOcean or Linode) you should consider how that domain name you are giving to Pixelfed is being resolved and routed. Are you using Traefik as a reverse proxy for your homelab server? Explaining that is beyond the scope of this article, but is important to think about!

For the database settings, you can use the MYSQL_* settings with MariaDB. There are MariaDB specific environment variables but when not supplied the MySQL ones are used instead. Make sure to set a strong database password (even for these internally-hosted services behind a reverse proxy!). If you change the database port, make sure other containers are appropriately configured.

## Databases (MySQL)
# pass the same values to the db itself

I left the Postgres values commented out in the file. You may choose to delete them, if you prefer.

For redis make sure to choose a strong password.

## Cache (Redis)

For ActivityPub federation, which allows you to follow different Pixelfed servers - and even Mastodon servers! - you can set these flags to true. If you’re simply experimenting and would rather not risk exposing your server to the Fediverse just yet, you can leave these set to their default FALSE values and flip them TRUE later on.

## ActivityPub Federation

Finally, a few miscellaneous settings I have enabled.

INSTANCE_PUBLIC_HASHTAGS=true should be allowing my hashtags to be available to all users (right now, it’s just me!).

INSTANCE_PUBLIC_LOCAL_TIMELINE=true enables a public timeline, so I can share my public posts.

STORIES_ENABLED=true Instagram-like Stories functionality, enabled mostly for fun.

APP_TIMEZONE=America/New_York to ensure my post times are accurate.

OPEN_REGISTRATION=false as I do not want rando’s registering on my personal Pixelfed instance. If I wanted to give someone access, I’ll create an account for them via the command line.

Setting up Services

These tasks need only be performed one time following the initial setup. You can run all of these commands in a specific container using this syntax:

docker-compose exec CONTAINER COMMAND

Which tells docker-compose to run COMMAND within the specified CONTAINER. Note that both the app and worker containers share an image and volume storage; running a command in either one should be fine and I have not noticed a difference.

To start, generate an APP_KEY using the app container:

docker-compose exec app php artisan key:generate

This key prints out to the command line and should be automatically inserted into your .env.docker configuration file. Verify by opening the file.

Next, link your storage:

docker-compose exec app php artisan storage:link

Run database migrations. This would need to be done again when upgrading Pixelfed to a newer version, as well.

docker-compose exec app php artisan migrate --force

Enable support for location on posts. This is just a simple list of common cities and does not include places.

docker-compose exec app php artisan import:cities

If you chose to enable ActivityPub Federation run the below. If not, you can always do this later.

docker-compose exec app php artisan instance:actor

According to Pixelfed’s docs, “Routes should be cached whenever the source code changes or whenever you change routes. Every time you edit your .env file, cache config.” So now run:

docker-compose exec app php artisan route:cache
docker-compose exec app php artisan view:cache
docker-compose exec app php artisan config:cache

Finally, we must deal with Job Queueing. I got this working with Laravel Horzion, and have no experience with or opinion on Queue Worker.

docker-compose exec worker php artisan horizon:install
docker-compose exec worker php artisan horizon:publish
docker-compose restart worker

Note that I’m switching to the worker container in the above commands. The purpose of this container is to run Laravel Horzion. You can see this in the docker-compose.yml line which tells the container to run it at start:

  command: gosu www-data php artisan horizon

The Pixelfed guide includes steps to setup a systemd job which runs Horizon. You need not do that with the docker setup. The worker container is handling the Horizon queues.

The final command above was to restart the worker container. Horizon is now running.

Side Notes

  • I skipped setting up email since I’m planning on this being a private instance just for me. On the cheap you could setup your gmail account with an app password, or you could configure any SMTP solution you like.

Web UI

My setup runs self-hosted on that little Intel NUC sitting atop my desk. I route traffic to it using Traefik Proxy.

I will not get into detail in this post on how to configure Traefik, but I will share my docker labels being used. These labels are applied on the app container.

  - "traefik.enable=true"
  - ""
  - "traefik.http.routers.pixelfed.rule=Host(``)"
  - "traefik.http.routers.pixelfed.entryPoints=secure"
  - "traefik.http.routers.pixelfed.tls=true"
  - "traefik.http.routers.pixelfed.service=pixelfed"
  - ""
  - ""

From top to bottom, these labels are doing the following:

  • Enabling this container and telling Traefik to route traffic to it via my web docker network.
  • Identifying that Traefik should send requests for the host coming in via secure entry point (TLS/443).
  • Naming the service, so in the final labels I can tell Traefik to route traffic to Pixelfed on port 80 via http.

Getting the first user

With Pixelfed up, running, and route-able you can now configure one or more users. I prefer to do this via the command line. I do not need to register users via the web and I do not need to worry about having a working email provider to send out user account confirmation emails.

This guide:

Contains a quick run-down of the user management commands.

docker-compose exec app php artisan user:create

Now, follow the prompts. Ensure that email verification = yes, to avoid having to send an email and confirm the user. If this is your first user / yourself, choose the Make Admin option.

Public profile pages

You can easily link to someone’s public profile. Images they post with Public visibility will show up here. Here is mine!

Other Excellent Resources

  1. Specific model is BOXNUC8i3BEK1. ↩︎