Workload · Running a Pixelfed instance

Pixelfed self hosted: a photo-sharing home for your community

To host Pixelfed yourself you need one Linux server running Docker, a domain name and some patience with a terminal: the project's own Docker Compose files start the website, the database, Redis and the background workers for you. Pixelfed is a photo-sharing site that joins the fediverse, so people on other servers, including Mastodon, can follow your members' pictures. This guide takes a photography club, a studio or a group of friends from an empty server to a running instance you control.

The short version
  1. Set up from the Compose files in the Pixelfed repository
  2. You become admin from the command line before the site goes public
  3. Sign-ups stay closed until you decide to open them
  4. Photos on the server's own disks, or in S3-compatible storage
  5. Open source under the AGPL-3.0 licence

Why run your own Pixelfed?

Because the pictures, the members and the rules stay yours. Each Pixelfed server, called an instance, hosts its own accounts and feeds. Through ActivityPub, the protocol fediverse servers use to talk to each other, the docs say Pixelfed sends and receives activity with other Pixelfed servers and with other software such as Mastodon. Your members post at home and still reach people elsewhere.

The other side of that freedom: you are the admin. Updates, backups and moderation are your job. If nobody in your group wants that, joining an existing instance is the easier choice.

What Pixelfed needs from a server

Under the hood, Pixelfed is a PHP web app built on Laravel. It keeps its data in a database, uses Redis for caching and its job queue, and uses image tools to shrink and optimise every upload, with ffmpeg making video thumbnails. The Docker route bundles all of that, so you mostly need disk space and a sensible amount of memory.

The written requirements are a little out of step, which is worth knowing before you choose a database or a PHP version:

  • PHP: the docs' prerequisites page asks for 8.3 or newer, while the current code requires 8.4 or 8.5 and the README recommends 8.5. The Docker image is built on 8.5, so you don't have to choose.
  • Database: the docs list MySQL, MariaDB and PostgreSQL. The README now gives official support to MySQL from version 8 and MariaDB from version 11, while PostgreSQL 14 and later is best effort and not recommended. The Compose file uses MySQL 9.
  • Memory and cores: neither the docs nor the README give a figure, so start modestly and watch it.
  • Disk: this is what grows. By default each photo or video can be up to 15000 KB and each account can hold up to 1000000 KB, which by our arithmetic is about a gigabyte.

The server that fits

For a club or a circle of friends, one of the compact servers on our dedicated server page is a sensible start, ideally one with NVMe storage. If you expect a busy instance with a lot of remote photos arriving, an everyday single-processor server gives the database and image workers more room. Every option is a whole physical computer that runs your instance and nobody else's, and you log in as root.

  • No bandwidth bill. Nobody meters your port. A picture that travels far across the fediverse leaves your invoice exactly as it was.
  • A way back in. Rack servers and blades include a console screen and power button that work even if the system won't start, and so do two cartridge models, m510 and m710x. Other cartridges give you power control only.
  • Ready quickly. A ready server is in your hands within 30 minutes of the payment being approved. A custom build takes longer.
  • IPv6 included. One IPv4 address comes with the server, and IPv6 at no charge.
  • Month to month. No contract, so you can move to a bigger machine when the community grows.

We suggest any line except the X-Gene ARM one: it runs only two old Ubuntu releases, 16.04 and 18.04, which are too old for the current Docker Engine this guide needs. Ubuntu on a regular Intel or AMD server is the simple path; our Linux servers page lists the systems you can install.

Up and running, step by step

First install Docker Engine and its Compose plugin, which the repository's setup guide expects. Version 28 or newer matters here: from Docker Engine 28, a port bound to 127.0.0.1 can only be reached from the server itself (moby#45610). Then run this. It turns the firewall on with IPv6 covered before any rule is added, lets SSH in, fetches Pixelfed, protects the settings file and keeps the app's port private.

sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw
sudo ufw allow OpenSSH
sudo ufw --force enable
sudo docker version --format 'Engine {{.Server.Version}}'
sudo git clone https://github.com/pixelfed/pixelfed /srv/pixelfed
cd /srv/pixelfed
sudo chown -R www-data:www-data storage/ bootstrap/cache/
sudo cp .env.docker.example .env && sudo chmod 600 .env
sudo sed -i 's/"8080:8080"/"127.0.0.1:8080:8080"/' docker-compose.yml
openssl rand -base64 32
  1. Why the port edit (our change, not in the setup guide): as shipped, the Compose file opens port 8080 on every address the server has, 0.0.0.0 for IPv4 and [::] for IPv6, and Docker's published traffic gets past ufw on IPv4 (on IPv6 as well, if you have turned on Docker's own IPv6 networking). With 127.0.0.1 in front, only the server itself can reach it.
  2. Open the settings with sudo nano .env. Put the random text from the last command after base64: as the app key, so the line reads APP_KEY="base64: followed by that text and a closing quote. The setup guide links a website for this; making the key on your own server keeps it off the internet.
  3. Set APP_URL to the full address with https, such as "https://photos.example.net", and both ADMIN_DOMAIN and APP_DOMAIN to the name alone, "photos.example.net".
  4. Choose two long random passwords, one for DB_ROOT_PASSWORD and one for DB_PASSWORD, and fill in your email provider's SMTP details. Keep every value in double quotes.
  5. Check that OPEN_REGISTRATION still says "false". Leave it that way for now.

Start it and make yourself admin

Next, build and start everything. The first build compiles some image and video tools, so give it time. Following the setup guide, the database and Redis start first and get 30 seconds to settle, then the rest follows: the website, the Horizon worker that runs background jobs, and the scheduler for regular housekeeping.

cd /srv/pixelfed
sudo docker compose build
sudo docker compose up -d db redis && sleep 30
sudo docker compose up -d
sudo docker compose exec pixelfed php artisan instance:actor
sudo docker compose exec pixelfed php artisan import:cities
sudo docker compose exec pixelfed php artisan passport:keys
sudo docker compose exec pixelfed php artisan passport:client --personal --no-interaction
sudo docker compose exec pixelfed php artisan user:create

The instance:actor command is what lets your server federate. The personal client line carries --no-interaction, our addition, so it uses its default name instead of pausing for one. The last command asks you questions one at a time: your name, a username, your email, and then your password twice (at least 6 characters, hidden as you type). Then come three questions: say yes to making the user an admin, yes to verifying the email, and yes to the final "Are you sure". If you skip that last yes, which is not the default, no user is created. Avoid the --password option: typed as an argument, your password would be saved in your history.

Doing this now, while the app only answers on the server itself and nothing public points at it, means your admin account exists before anyone else can open the site.

Putting it on the web

Use apt to add nginx plus certbot with its nginx plugin, then save this as /etc/nginx/sites-available/pixelfed with your own name in it. It follows the setup guide's proxy settings. One difference is ours: the guide points at localhost:8080, and we use 127.0.0.1, because the app now listens only on 127.0.0.1, the IPv4 loopback address, while localhost can resolve to the IPv6 address ::1 first.

server {
    listen 80;
    listen [::]:80;
    server_name photos.example.net;
    client_max_body_size 500M;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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_read_timeout 300s;
    }
}
  1. Turn the site on with a symlink to it from the sites-enabled folder. Check the syntax with sudo nginx -t; if it reports ok, run sudo systemctl reload nginx.
  2. Let web visitors in: sudo ufw allow 80/tcp, then sudo ufw allow 443/tcp. nginx runs directly on the server, so until these rules exist ufw turns visitors away on both IPv4 and IPv6.
  3. At your DNS provider, point the name at the server: an A record for its IPv4 address, plus an AAAA record when the server has IPv6. Wait until dig +short photos.example.net answers with the server's address, then get a certificate: sudo certbot --nginx -d photos.example.net.
  4. Sign in as your admin. The configuration reference says that with ENABLE_CONFIG_CACHE switched on, settings can be managed in the admin dashboard, so look there too and make sure registration is still closed.

Where your photos live, and keeping copies

Out of the box, uploads are saved in the storage folder on the server's own disks. If you'd rather use S3 or another S3-compatible store, set PF_ENABLE_CLOUD to "true" and fill in the AWS_ lines in .env (key, secret, region, bucket, URL, endpoint and path style). Our own reasoning: photos kept in a bucket are not in a copy of the server, so that bucket needs its own backup plan.

Our suggested routine has three parts, run from /srv/pixelfed. First, a database dump: sudo docker compose exec -T db sh -c 'exec mysqldump --single-transaction --no-tablespaces -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' | gzip | sudo tee /root/pixelfed-db.sql.gz > /dev/null. The password comes from inside the database container, so it stays out of your history. mysqldump will still print a warning about it, because for as long as the dump runs it can appear among the running processes, both on the server itself and inside the container. Second, the files: sudo tar -czf /root/pixelfed-files.tar.gz storage .env, because .env holds your app key and passwords. Third, copy both somewhere other than this server. You manage the server; we keep the hardware and the network running.

A copy you have never restored is a hope rather than a backup. Two quick checks show the copies aren't damaged: sudo gunzip -t /root/pixelfed-db.sql.gz for the dump and sudo tar -tzf /root/pixelfed-files.tar.gz > /dev/null for the files. They don't show that Pixelfed comes back. For that, use a spare server: check both files arrived (sudo ls -l, plus sudo gunzip -t on the dump), run only the first block of the setup (clone, ownership, copying .env, the port edit) and start nothing yet. In /srv/pixelfed, unpack the archive with sudo tar -xzf /root/pixelfed-files.tar.gz, which puts back your original .env and your photos. The order matters: the database image sets up its users from .env the first time it starts, so it must never start before the backed-up file is in place. Only now run sudo docker compose build, start the database on its own (sudo docker compose up -d db) and wait about 30 seconds. Then feed the dump in: sudo zcat /root/pixelfed-db.sql.gz | sudo docker compose exec -T db sh -c 'exec mysql -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"', start the rest, and sign in.

Updates and the licence

Before every update, take a backup. Then, in /srv/pixelfed, run sudo git pull, then sudo docker compose build, and finally sudo docker compose up -d. From our reading of the Compose file (the docs don't say this), the web container applies any new migrations to the database each time it starts up. Because you edited the port line, git may refuse the pull if the project changed that file too; sudo git stash, pull again, sudo git stash pop, then check the 127.0.0.1 is still there.

Pixelfed is free software under the AGPL-3.0, as its LICENSE file states. In plain terms, as we read it, and only as a general reading rather than legal advice: you can run it for your community as it comes. If you change the code and people use your changed version over the network, whoever runs it must offer those people the changed source. And anyone who passes copies on to others, changed or not, must make the source available with them. Those duties fall on whoever runs or hands out the software.

Running something similar for discussions rather than pictures? Our guide to Lemmy for your community follows the same approach.

Questions people ask

Can I install Pixelfed without Docker?
Yes. The documentation walks through a manual setup with PHP-FPM and nginx or Apache, a systemd service that keeps the Horizon worker running, and a cron entry that runs the scheduler every minute. The README now recommends Docker Compose, and Docker spares you matching PHP versions and image tools by hand.
How do I stop random people creating accounts?
Leave OPEN_REGISTRATION set to false, as the Docker example file already has it. Create your own admin account first from the command line, then check the registration setting in the admin dashboard as well. When you are ready to welcome members, you can open sign-ups and cap them with PF_MAX_USERS.
Will people on Mastodon see our photos?
Yes, once federation is on. The Docker example switches on AP_REMOTE_FOLLOW along with ACTIVITY_PUB, and instance:actor sets up your server's identity. After that, Pixelfed exchanges activity with other fediverse software, Mastodon included, according to its docs.
Do I have to keep photos on the server?
No. With PF_ENABLE_CLOUD and the AWS_ settings, uploads go to S3 or an S3-compatible store. Remember that a database backup doesn't include that bucket, so give it its own copies.
Which database should I pick?
Stay with what the Compose file sets up, MySQL 9. According to the README, MySQL from 8 and MariaDB from 11 are officially supported, and PostgreSQL support is best effort and not recommended.

Give your photo community a home

Choose a server with room for your pictures, install Ubuntu, and follow this guide.

Get a server for your Pixelfed instance

Read next