Migrating Pixelfed (Part Two)
I went back to my old VPS to retrieve the data, first doing a SQL dump and then scp-ing the entire storage/app/public directory that housed all the uploaded photos, avatars and thumbnails. My userbase and content after a year was quite modest, especially given that I had neglected it for months on end, but I didn't want to start from scratch.
Going back to my new Oracle VPS, I imported the backup.sql file and then dropped the contents of the copied directory into the mapped filesystem directory for the Pixelfed Glitch docker container, located at ./docker-compose-state/data/pixelfed/storage/app/public
Logging into the app, it was running-ish, but logs were showing constant 500 errors. Investigating the logs further, the internal API calls were failing due to authorization errors. It turns out I also needed to regenerate the oauth keys, as well as chmod 600 and chown www-data on them. And with that, the site was stable!
From here it was a matter of domain management. It may be a bit of overkill, but I transferred the domain to Cloudflare. This meant that Traefik was no longer required to call LetsEncrypt to generate the origin server certificates. Traefik was still managing the reverse proxy within the VPS, but now I had to install the Cloudfare certificates.
# traefik.yml -- static configuration
# ...
providers:
file:
directory: /etc/traefik/dynamic
watch:true
# docker-compose.yml -- traefik instance configuration
# ...
services:
traefik:
command:
- "--configFile=/etc/traefik/traefik.yml"
volumes:
- ./traefik.yml:/etc/traefik/traefik.yml
- ./dynamic:/etc/traefik/dynamic:ro
- ./certs:/etc/traefik/certs:roThe watched /dynamic directory housed a single tls.yml file that points to the volume-mapped /etc/traefik/certs directory, containing the files from Cloudflare.
With that in place, I updated the pixelfed-glitch Docker labels to route the domain, no longer needing to include a label for the TLS certificate resolver since those files were now in place. I also needed to update the .env files to reflect the domain through the app.
Because the app's DNS record changed, it meant that the mailer was no longer configured. I use an external mailer to handle things like account email verification, password resets, that sort of thing. mailtrap has a generous free tier that grants one domain, but after I updated the DNS records on mailtrap's domain configuration to point to Cloudflare, the pixelfed logs were unable to send emails due to an authorization error.
It turns out that mailtrap had changed their SMTP requirements, and the pixelfed documentation (neither the main one nor the fork I was using) had reflected that change: namely, mailtrap now needed a username (api or smtp@mailtrap.io) and a password (the domain's API key in the mailtrap credentials) and also had a new SMTP host live.smtp.mailtrap.io.
The welcome email finally landed in my inbox, but in the pixelfed dashboard, there were a handful of test accounts I was using that were stuck in a sort of limbo where they didn't have user IDs yet, but were awaiting their own email verification (an email that was never sent). I needed a way to remove these users from curated onboarding.
// app/Console/Commands/CuratedDelete.php
<?php
namespace App\Console\Commands;
use App\Models\CuratedRegister;
use Illuminate\Console\Command;
class CuratedDelete extends Command
{
protected $signature = 'curated:delete {identifier : ID or email of the pending applicant}';
protected $description = 'Delete a pending curated onboarding applicant by ID or email';
public function handle()
{
$identifier = $this->argument('identifier');
$record = CuratedRegister::where('id', $identifier)
->orWhere('email', $identifier)
->first();
if (!$record) {
$this->error('No pending curated applicant found with ID or email: ' . $identifier);
return 1;
}
$this->info("Found applicant: {$record->username} ({$record->email})");
if ($this->confirm('Delete this pending applicant?')) {
$record->delete();
$this->info('Applicant deleted successfully.');
return 0;
}
$this->info('Aborted.');
return 0;
}
}This gave me a new command php artisan curated:delete <id|email> that I could use to manually clear off the limbo users.
It took about two days total, but I was finally able to move off of the expensive non-promo-priced VPS and onto a more robust, extensible and easily-configurable setup.