artectrex's blog - Set up your own Pixelfed instance with Docker

Using Docker, docker-compose and nginx to set up your own Pixelfed instance

đź“… 25 Jan 2021

Since starting on PixelDroid, at multiple occasions I wanted to try things out or try to contribute to Pixelfed, but each time gave up on getting the server to work after a lot of frustration.

So now that I finally got it working, let me share a step by step guide on how to do it.

Requirements

First steps

First make a new folder:

mkdir pixelfed_docker_compose

cd pixelfed_docker_compose

And clone the pixelfed repo inside of that folder:
git clone https://github.com/pixelfed/pixelfed

Creating the docker image

Let’s build the docker image we are going to use:

cd pixelfed

docker build . -t pixelfed:{Current Date} -f contrib/docker/Dockerfile.apache

Replace {Current Date} with the current date, today it would for example be
docker build . -t pixelfed:20210125 -f contrib/docker/Dockerfile.apache

This just tags the image we’re building here, so that in the future we know which is which.

Creating the docker-compose.yml

Below is my docker-compose.yml. Read through it carefully so you understand what goes where.

The docker-compose file
version: '3'
services:

  app:
    image: pixelfed:20210125 #replace with the tag you just made
    restart: unless-stopped
    ports:
      - "8080:80" #80 is the port inside the container, 8080 is the port outside
    env_file:
      - ./.env
    volumes:
      - "pixelfed-storage:/var/www/storage"
      - "app-bootstrap:/var/www/bootstrap"
      - ./.env:/var/www/.env # we'll be creating a .env file in a bit
    networks:
      - web
      - pixelfed

  db:
    image: mysql:8.0 # You can probably also get it to work with mariadb
    restart: unless-stopped
    networks:
     - pixelfed
    command: --default-authentication-plugin=mysql_native_password
    volumes:
     - db-data:/var/lib/mysql
    environment:
     - MYSQL_DATABASE=pixelfed
     - MYSQL_USER=${DB_USERNAME}
     - MYSQL_PASSWORD=${DB_PASSWORD}
     - MYSQL_RANDOM_ROOT_PASSWORD=true

  worker:
    image: pixelfed:20210125 #replace with the tag you just made
    restart: unless-stopped
    env_file:
      - ./.env
    volumes:
      - "pixelfed-storage:/var/www/storage"
      - "app-bootstrap:/var/www/bootstrap"
    networks:
      - web  # Required for ActivityPub
      - pixelfed
    command: gosu www-data php artisan horizon

  redis:
    image: redis:5-alpine
    restart: unless-stopped
    volumes:
      - "redis-data:/data"
    networks:
      - pixelfed

volumes: # I'm using docker volumes here, if you want things to be stored elswhere you can change that here
  redis-data:
  app-bootstrap:
  pixelfed-storage:
  db-data:

networks:
  pixelfed:
    internal: true
  web:
    external: true

Save the this to a docker-compose.yml file of your own inside the pixelfed_docker_compose folder you created earlier.

.env file: create the settings of the instance

Below is my .env file. Change the values to your desired configuration.

You might want to check out the .env.example file in the pixelfed repo you just cloned, in case since the posting of this article something changed that requires an updated .env file, but at the time of writing this one worked fine.

.env file
APP_NAME="PixelDroid testing instance"
APP_ENV=production
APP_KEY= #leave empty, this will be generated later!
APP_DEBUG=false

#change this and the following 3 to your own domain
APP_URL=https://testing.pixeldroid.org
APP_DOMAIN="testing.pixeldroid.org"
ADMIN_DOMAIN="testing.pixeldroid.org"
SESSION_DOMAIN="testing.pixeldroid.org"
TRUST_PROXIES="*"

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=pixelfed
DB_USERNAME=pixelfed
#change this to a random password
DB_PASSWORD= CHANGE_ME

BROADCAST_DRIVER=log
CACHE_DRIVER=redis
SESSION_DRIVER=redis
QUEUE_DRIVER=redis

REDIS_SCHEME=tcp
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

# I'm using the log driver for this testing instance, so the following settings are irrelevant. For a real instance you'll want to change it to something else, see pixelfed documentation
MAIL_DRIVER=log
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="pixelfed@example.com"
MAIL_FROM_NAME="Pixelfed"

OPEN_REGISTRATION=true
ENFORCE_EMAIL_VERIFICATION=true
PF_MAX_USERS=1000

MAX_PHOTO_SIZE=64000
MAX_CAPTION_LENGTH=150
MAX_ALBUM_LENGTH=4
MAX_ACCOUNT_SIZE=10000000
IMAGE_QUALITY=100

ACTIVITY_PUB=true
AP_REMOTE_FOLLOW=true
AP_INBOX=true
PF_COSTAR_ENABLED=false

HORIZON_EMBED=true
OAUTH_ENABLED=true

Current folder structure

This is the folder structure you should have now:

pixelfed_docker_compose
    pixelfed/
    .env
    docker-compose.yml

Start up the containers

Let’s create the networks we declared in the docker-compose.yml:

docker network create web

First run docker-compose up -d. This will start the containers as they were configured in the docker-compose file.

Then we are going to generate the key that we left empty in the .env file:
docker-compose exec app php artisan key:generate
Respond yes to the prompt.

Check that a key was generated in the .env file.

Now, let’s restart the container we just used to generate the .env file, make sure the new .env file is taken into account (config:cache) and do the database migration.

docker-compose restart app
docker-compose exec app php artisan config:cache
docker-compose exec app php artisan migrate

Respond yes to the prompt.

Configuring nginx

Make a new file in your /etc/nginx/sites-enabled directory (or wherever your nginx config looks for them, maybe in etc/nginx/conf.d/ depending on your configuration), to reverse proxy the requests to your domain to the right port (in this case, 8080), and to terminate SSL.

See below for my config file:

nginx config file

server {
    listen 80;
    listen [::]:80;
    server_name testing.pixeldroid.org;
    # enforce https
    return 301 https://$server_name$request_uri;
}


server {
    server_name testing.pixeldroid.org;
    listen [::]:443 ssl;
    listen 443 ssl;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/testing.pixeldroid.org/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/testing.pixeldroid.org/privkey.pem; # managed by Certbot

    client_max_body_size 100M;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $http_x_forwarded_host;
        proxy_set_header X-Forwarded-Port $http_x_forwarded_port;
        proxy_redirect off;
        proxy_pass   http://127.0.0.1:8080;
    }
}

  

Of course you should change the domain name, make sure the port is the one you chose before in the docker-compose file, and configure your own certificates (I let certbot do it, which is pretty convenient since it’ll also do renewals). Make sure the client_max_body_size value is bigger than the value you set in the .env.

Restart nginx (sudo systemctl restart nginx), or just make it reload the config files (sudo nginx -s reload).

Finishing touches

At this point, you should be able to access your instance on your domain. Check if it looks alright, especially make sure that it doesn’t have everything shown twice (which was a very weird issue I had for a while when some things were misconfigured). If you do have that issue, uncollapse the accordion below:

Look here to fix the "everything shown twice", and maybe other issues if the front page seems broken

First stop the containers (docker-compose down).

Delete all the volumes except the one for the database (docker volume rm pixelfeddockercompose_redis-data pixelfeddockercompose_app-bootstrap pixelfeddockercompose_pixelfed-storage, but the volumes might have different names for you: find out their names with docker volume ls).

Then start the containers up again: docker-compose up -d

Now, you can create a new user. Go back to the folder with the docker-compose file in it, and run this:
docker-compose exec app php artisan user:create

Answer the questions as you wish, for the first account (yours) you probably want to answer the last three questions like this:

 Make this user an admin? (yes/no) [no]:
 > yes

 Manually verify email address? (yes/no) [no]:
 > yes

 Are you sure you want to create this user? (yes/no) [no]:
 > yes

Enable OAuth and API access so that apps can work

This was the most important part for me, since after all the main point of setting up an instance was to be able to debug and play with the API responses in the context of my work on PixelDroid.

As you might have noticed in the .env file, I set OAUTH_ENABLED=true so you would think this would enable the API.

However some additional steps are necessary, or you will get very generic errors. I only figured them out after coming accross the 0.10.6 beta release notes.

Go look at those release notes, or just follow the steps here:

docker-compose exec app php artisan passport:keys

Make sure you have OAUTH_ENABLED=true in the .env

docker-compose exec app php artisan config:cache
docker-compose exec app php artisan route:cache
docker-compose exec app php artisan passport:client --personal

Just answer whatever to the prompt of that last command, I’m not ever sure it is necessary.

Upgrading your Pixelfed instance to a new version

First update the Pixelfed source and then build a new image (again, replace the date with the current date here):

cd pixelfed

git pull

docker build . -t pixelfed:20210203 -f contrib/docker/Dockerfile.apache

cd ..

To update the images used inside the docker-compose, replace the dots with the images you use (For instance, I ran docker pull mysql:8.0 redis:5-alpine):

docker pull ....

Normally you would use docker-compose pull here to get all of them, but the way I tagged our own pixelfed image breaks that, since of course there is nothing there to be pulled.

Next we want to actually use the new image: just edit the docker-compose.yml and replace the old image with the new one we just built.

Now, let’s delete and recreate all of our containers so that we use the latest versions we just made. This doesn’t delete any of the named volumes where the data actually is (remember, the docker way of doing things means your containers don’t store any data but just the application, so deleting the containers doesn’t delete any of your data)

docker-compose down

docker-compose up -d

Pixelfed is now updated, but we still need to migrate from the previous version to this one:

In any case you should run

docker-compose exec app php artisan migrate

Depending on the version you come from and the new one you just installed, you will have to run some more commands. Check the release notes for more info.
To go from 0.10.9 to 0.10.10 I had to run the following (all preceded by docker-compose exec app to run it in the app container of course):

composer install

php artisan migrate --force

php artisan config:cache

php artisan route:cache

php artisan instance:actor