Looking to set up your own Pleroma instance? This guide should walk you through everything you need to do to make it happen. Of note, this assumes you have some familiarity with Docker and working with it, PostgreSQL and Linux. I also utilize Traefik to handle proxying requests.

First, most of this guide is the same thing that can be found here with a few changes. The reason I don't just tell you to go to that guide is that I have an already existing PostgreSQL installation along with a pre-existing network setup in my Docker stack.


To start with this is my docker-compose.yml file:

    build: .
    image: pleroma
    container_name: pleroma
    hostname: pleroma.mydomain.com
      - TZ=${TZ}
      - UID=${PUID}
      - GID=${PGID}
      - 4001:4000
      - /etc/localtime:/etc/localtime:ro
      - ${DOCKERCONFDIR}/pleroma/uploads:/pleroma/uploads
      - postgres
    restart: unless-stopped
      - SYS_PTRACE
    image: postgres:9.6
    container_name: postgres
    hostname: postgres.mydomain.com
      - PGID=${PGID}
      - PUID=${PUID}
      - TZ=${TZ}
      - /etc/localtime:/etc/localtime:ro
      - ${DOCKERCONFDIR}/postgresql/pg_data:/var/lib/postgresql/data
      - ${DOCKERCONFDIR}/postgresql/root:/root
      - 5432:5432
    restart: unless-stopped
      - SYS_PTRACE
      driver: bridge
          - subnet:
version: "2.4"

While this isn't the full configuration file, these are the parts which allow Pleroma to function. As noted above I already have a PostgreSQL instance and a pre-existing network created for my Docker stack. You should also note that I use some variables here, mostly in the environment and volumes sections for each container. You can use them too, or swap them out for their "real" values. I'd recommend using the variables with an .env file, but it's up to you.

PostgreSQL Setup

The first thing I do is create a new PostgreSQL user for Pleroma:

psql -U superuser -h localhost -p 5432

You'll want to change out 'superuser' for a user which can create users within PostgreSQL. The follow will create a database for Pleroma, a PostgreSQL user and allow access to the new database for that user.


It appears the database setup processes creates an EXTENSION, so you'll need to provide your PostgreSQL user with the superuser permission. You can do so by running the following:


File System Setup

Once setting up PostgreSQL has been completed, you'll want to setup your uploads folder. I've set mine up at /home/jimmy/.docker/config/pleroma/uploads. Next, I setup a folder where I will build the Pleroma Docker image. I've done this at /home/jimmy/.docker/builds/pleroma. Within that directory, I create a new Dockerfile and place the follow contents into it:

FROM elixir:1.9-alpine

ENV UID=911 GID=911 \


RUN apk -U upgrade \
    && apk add --no-cache \
       build-base \

RUN addgroup -g ${GID} pleroma \
    && adduser -h /pleroma -s /bin/sh -D -G pleroma -u ${UID} pleroma

USER pleroma
WORKDIR pleroma

RUN git clone -b develop https://git.pleroma.social/pleroma/pleroma.git /pleroma \
    && git checkout ${PLEROMA_VER}

COPY config/secret.exs /pleroma/config/prod.secret.exs

RUN mix local.rebar --force \
    && mix local.hex --force \
    && mix deps.get \
    && mix compile

VOLUME /pleroma/uploads/

CMD ["mix", "phx.server"]

This Dockerfile is different than the one provided by the above linked GitHub respository. The difference is the first line. I am utilizing a newer version of elixer which is required. If you do not use this you will likely see the following error in your logs when trying to startup your Pleroma instance:

15:38:00.284 [info] Application pleroma exited: exited in: Pleroma.Application.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (RuntimeError) 
            !!!OTP VERSION WARNING!!!
            You are using gun adapter with OTP version, which doesn't support correct handling of unordered certificates chains. Please update your Erlang/OTP to at least 22.2.
            (pleroma) lib/pleroma/application.ex:57: Pleroma.Application.start/2
            (kernel) application_master.erl:277: :application_master.start_it_old/4
15:38:22.720 [info]  SIGTERM received - shutting down

Next create a config folder within your builds/pleroma directory. For example, my full path is /home/jimmy/.docker/builds/pleroma/config. Within there create a file called secret.exs. Open this file in your favorite text editor and paste in the following:

use Mix.Config

config :pleroma, Pleroma.Web.Endpoint,
   http: [ ip: {0, 0, 0, 0}, ],
   url: [host: "pleroma.domain.tld", scheme: "https", port: 443],
   secret_key_base: "<use 'openssl rand -base64 48' to generate a key>"

config :pleroma, :instance,
  name: "Pleroma",
  email: "admin@email.tld",
  limit: 5000,
  registrations_open: true

config :pleroma, :media_proxy,
  enabled: false,
  redirect_on_failure: true,
  base_url: "https://cache.domain.tld"

# Configure your database
config :pleroma, Pleroma.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "pleroma",
  password: "pleroma",
  database: "pleroma",
  hostname: "postgres",
  pool_size: 10

Ensure that you update the host in the url line, the secret_key_base, the name, email, and the database information. Save and exit the file.

Building The Pleroma Docker Image

Alright, so with your Dockerfile and Pleroma configuration files in place we need to build the image! While in the same directory as your Dockerfile, run the following command:

docker build -t pleroma .

This may take a few minutes to complete. Once completed we need to setup the database within PostgreSQL. This is another reason I am putting together this guide, because using the command from the other GitHub repository will not work.

docker run --rm -it --network=main_static pleroma mix ecto.migrate

That will take 30-45 seconds to run. Once completed we need to generate our web push keys. Use the following command in order to do so:

docker run --rm -it --network=main_static pleroma mix web_push.gen.keypair

Copy the output from the above command, and place it at the bottom of your config/secret.exs file. Now we need to rebuild the Pleroma Docker image again with this new configuration, so do so using:

docker build -t pleroma .

You should be all set now! You just need to run docker-compose up -d and it should get everything started up. If all went well you should see something similar to:

Pleroma Instance

Updating Your Pleroma Instance

So you've got your instance up and running but how about keeping it up to date! Fortunately this is relatively easy as well! Just go into the directory with your Pleroma Dockerfile and run the following commands:

docker stop pleroma
docker build --no-cache -t pleroma .
docker run --rm -it --network=main_static pleroma mix ecto.migrate

Now run docker-compose up -d and a new container will be created with your newly built image!



This guide assumes:

  • You've already gotten a server setup with Ubuntu 19.10
  • Nginx
  • MySQL 5.6+ (or MariaDB 10.2.7+)
  • PHP 7.2+ with the following extensions bcmath, ctype, curl, exif, iconv, imagick, intl, json, mbstring, mysql, gd, openssl, tokenizer, xml and zip
  • Redis
  • ImageMagick
  • Supervisor
  • Git

I would also recommend installing JPEGOptim, OptiPNG and PNGQuant. These will strip exif data and optimizes jpeg and png photos.

$ apt install jpegoptim
$ apt install optipng
$ apt install pngquant

Here's a list of the packages I have installed:








git/eoan-updates,eoan-security,now 1:2.20.1-2ubuntu1.19.10.1 amd64 [installed,automatic]
composer/eoan,now 1.9.0-2 all [installed]
supervisor/eoan,now 3.3.5-1 all [installed]
libhiredis0.14/eoan,now 0.14.0-3 amd64 [installed,automatic]
php-redis/eoan,now 5.2.1+4.3.0-1+ubuntu19.10.1+deb.sury.org+1 amd64 [installed]
redis-server/eoan,now 5:5.0.5-2build1 amd64 [installed]
redis-tools/eoan,now 5:5.0.5-2build1 amd64 [installed,automatic]
sendmail-base/eoan,now 8.15.2-13 all [installed,automatic]
sendmail-bin/eoan,now 8.15.2-13 amd64 [installed,automatic]
sendmail-cf/eoan,now 8.15.2-13 all [installed,automatic]
sendmail/eoan,now 8.15.2-13 all [installed]


First create a new user:

$ adduser pixelfed

Login as that user via SSH.

Now, clone the Pixelfed repository, move into it and use composer to install necessary packages:

$ cd ~
$ git clone https://github.com/pixelfed/pixelfed
$ cd pixelfed
$ composer install

Next we'll need to create a configuration file for the site:

$ cp .env.example .env
$ php artisan key:generate

Next open the .env file in your editor of choice and update it so it matches your configuration. Of note, I utilized Mailgun for my email service, however I could not get it working using the 'mailgun' driver. I had to use the 'smtp' driver with SMTP credentials I created from the Mailgun dashboard. Here's what I have for my mail configuration:


Once you've updated your .env file, save it. Next we need to get the database setup. This is how I created my MySQL user and database.

CREATE DATABASE pixelfed CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'pixelfed'@'localhost' IDENTIFIED BY 'PASSWORD';
GRANT ALL PRIVILEGES ON pixelfed.* TO 'pixelfed'@'localhost';

Run the command php artisan migrate. This will create the necessary structure and stuff in the MySQL database. Make sure to answer 'yes' to any prompt(s) that come up. It may take a minute or two to complete.

Next run the following commands (especially if you're running a production environment):

$ php artisan config:cache
$ php artisan route:cache
$ php artisan view:cache

Next up is install Horizon. This will manage jobs that the application has (things like resizing images, deleting images, etc...):

php artisan horizon:install

Next we'll want to create a supervisor configuration for Horizon. I do this as my root user. Create the file /etc/supervisor/conf.d/horizon.conf and place the following inside:

command=php /home/pixelfed/pixelfed/artisan horizon

I recommend running the following commands (it will restart and enable the service):

$ systemctl enable supervisor
$ systemctl restart supervisor

If you run a ps auxfwww you should see something similar:

root     18629  0.0  0.2  27672 20696 ?        Ss   12:46   0:01 /usr/bin/python2 /usr/bin/supervisord -n -c /etc/supervisor/supervisord.conf
pixelfed 18651  0.1  0.7 136724 57568 ?        S    12:46   0:04  \_ php /home/pixelfed/pixelfed/artisan horizon
pixelfed 18658  0.1  0.7 136724 57844 ?        S    12:46   0:05      \_ /usr/bin/php7.3 artisan horizon:supervisor hostname-tixv:supervisor-1 redis --delay=0 --memory=128 --queue=high,default,feed --sleep=3 --timeout=60 --tries=3 --balance=auto --max-processes=20 --min-processes=1 --nice=0
pixelfed 18672  0.0  0.6 134676 56724 ?        S    12:46   0:01          \_ /usr/bin/php7.3 artisan horizon:work redis --delay=0 --memory=128 --queue=high --sleep=3 --timeout=60 --tries=3 --supervisor=hostname-tixv:supervisor-1
pixelfed 18677  0.0  0.7 138968 58944 ?        S    12:46   0:01          \_ /usr/bin/php7.3 artisan horizon:work redis --delay=0 --memory=128 --queue=default --sleep=3 --timeout=60 --tries=3 --supervisor=hostname-tixv:supervisor-1
pixelfed 18682  0.0  0.6 134676 56192 ?        S    12:46   0:01          \_ /usr/bin/php7.3 artisan horizon:work redis --delay=0 --memory=128 --queue=feed --sleep=3 --timeout=60 --tries=3 --supervisor=hostname-tixv:supervisor-1

Next (and you can do this as your root user or pixelfed user), add a cronjob to the pixelfed user:

* * * * * cd /home/pixelfed/pixelfed && php artisan schedule:run >> /dev/null 2>&1

You can read this for more information on what sorts of things are done during this scheduled task.

Post-Installation Tasks

Import Cities

Run the command php artisan import:cities to import cities into your database. This will allow users to select locations when they're posting content.

Configure Nginx

Create a file - /etc/nginx/sites-available/mydomain.com.conf with the following contents:

server {
    server_name mydomain.com;
    listen 80;
    return 301 https://$host$request_uri;

server {
    listen 443 ssl http2;
    server_name mydomain.com;
    root /home/pixelfed/pixelfed/public;

    ssl_certificate /etc/letsencrypt/live/mydomain.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/mydomain.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    client_max_body_size 20m;
    client_body_buffer_size 128k;

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass unix:/run/php/php7.3-pixelfed-fpm.sock;
        fastcgi_index index.php;
        include fastcgi.conf;

    location ~ /\.(?!well-known).* {
        deny all;

Of course you'll want to swap out mydomain.com in the file name and file contents with your domain.

Next, we want to create a PHP-FPM pool just for our pixelfed user. So create a file /etc/php/7.3/fpm/pool.d/pixelfed.photos.conf and place the following inside of it:

user = pixelfed
group = pixelfed
listen = /run/php/php7.3-pixelfed-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic 
pm.max_children = 75 
pm.start_servers = 10 
pm.min_spare_servers = 5 
pm.max_spare_servers = 20 
pm.process_idle_timeout = 10s

Now, we're going to run these commands to enable Nginx and PHP-FPM and restart them:

$ systemctl enable nginx
$ systemctl restart nginx
$ systemctl enable php7.3-fpm
$ systemctl restart php7.3-fpm

You should now have a running system! Go ahead and visit your site, and sign up for a new account. If you've enabled email verification make sure you complete that. Once you're verified go back into SSH as the pixelfed user and into /home/pixelfed/pixelfed and run php artisan user:admin username_here. This will turn your account into an admin account. Once that is done you can visit the admin area of your site (it's linked in the dropdown menu in the upper right corner) as well as see the Horizon dashboard. It should show as Active.

Pixelfed Horizon Dashboard

I also recommend running php artisan storage:link.


The follow is several resources I used and should help you as well.


  • PHP 7.1+
  • Web server (ex. Apache or Nginx)
  • CMaNGOS instance


You can install the libary via composer:

composer require laizerox/php-wowemu-auth


Registering Accounts

First you'll want to use Composer's autoloader. Place this at the top of your script. This also calls to the library we need.

require_once __DIR__ . '/vendor/autoload.php';
use Laizerox\Wowemu\SRP\UserClient;

Next create the verifier and salt values using the username and password which your user submitted on your registration form.

$client = new UserClient($username);
$salt = $client->generateSalt();
$verifier = $client->generateVerifier($password);

Once that information is generated insert those values into the realmd database to the v and s fields.

Logging In

First, use Composer's autoloader. Place this at the top of your script. As noted above, this also the library.

require_once __DIR__ . '/vendor/autoload.php';
use Laizerox\Wowemu\SRP\UserClient;

Next you'll need to generate your "verifier". Think of this as the hashed version of the password your user put into the password field of you login form.

$client = new UserClient($username, $saltFromDatabase);
$verifier = strtoupper($client->generateVerifier($password));

Next, compare that value with the value stored in your CMaNGOS realmd.account table. You can see below for more of an example.



This example goes over how a user can register via a web form.


/* register.php */

require_once __DIR__ . '/vendor/autoload.php';
use Laizerox\Wowemu\SRP\UserClient;

/* Connect to your CMaNGOS database. */
$db = new mysqli($dbHost, $dbUser, $dbPassword, $dbName);

/* If the form has been submitted. */
if (isset($_POST['register'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];

    /* Grab the users IP address. */
    $ip = $_SERVER['REMOTE_ADDR'];

    /* Set the join date. */
    $joinDate = date('Y-m-d H:i:s');

    /* Set GM Level. */
    $gmLevel = '0';

    /* Set expansion pack - Wrath of the Lich King. */
    $expansion = '2';

    /* Create your v and s values. */
    $client = new UserClient($username);
    $salt = $client->generateSalt();
    $verifier = $client->generateVerifier($password);

    /* Insert the data into the CMaNGOS database. */
    mysqli_query($db, "INSERT INTO account (username, v, s, gmlevel, email, joindate, last_ip, expansion) VALUES ('$username', '$verifier', '$salt',  '$gmLevel', '$email', '$joinDate', '$ip', '$expansion')");

    /* Do some stuff to let the user know it was a successful or unsuccessful attempt. */


You'll want to do some error checking and validation, but that'll be left up to you.

The following is a very basic HTML form that can be used for registering an account.

<form action="/register" method="post">
    <input type="text" name="username" placeholder="Username">
    <input type="email" name="email" placeholder="Email Address">
    <input type="password" name="password" placeholder="Password">
    <?php $register = sha1(time()); ?>
    <input type="hidden" name="register" value="<?php echo $register; ?>">
    <button type="submit">Register</button>



/* login.php */

require_once __DIR__ . '/vendor/autoload.php';
use Laizerox\Wowemu\SRP\UserClient;

/* Connect to your CMaNGOS database. */
$db = new mysqli($dbHost, $dbUser, $dbPassword, $dbName);

/* Function to get values from MySQL. */
function getMySQLResult($query) {
    global $db;
    return $db->query($query)->fetch_object();

/* If the form has been submitted. */
if (isset($_POST['login'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];

    /* Get the salt and verifier from realmd.account for the user. */
    $query = "SELECT s,v FROM account WHERE username='$username'";
    $result = getMySQLResult($query);
    $saltFromDatabase = $result->s;
    $verifierFromDatabase = strtoupper($result->v);

    /* Setup your client and verifier values. */
    $client = new UserClient($username, $saltFromDatabase);
    $verifier = strtoupper($client->generateVerifier($password));

    /* Compare $verifierFromDatabase and $verifier. */
    if ($verifierFromDatabase === $verifier) {
        /* Do your login stuff here, like setting cookies/sessions... */
    else {
        /* Do whatever you wanna do when the login has failed, send a failure message, redirect them to another page, etc... */


Again, you'll want to add in your own error checking and validation but this should get you started. The below is a basic HTML form that can be used for logging in.

<form action="/login" method="post">
    <input type="text" name="username" placeholder="Username">
    <input type="password" name="password" placeholder="Password">
    <?php $login = sha1(time()); ?>
    <input type="hidden" name="login" value="<?php echo $login; ?>">
    <button type="submit">Sign In</button>

If you find any defects when using the library please open a new issue the Laizerox/php-wowemu-auth repository. If you need further assistance we can try assising you in the #offtopic channel of the CMaNGOS Discord server.

I used a basic VM to get this going. You can likely do it with less. It has 2GBs of RAM, 80GBs of disk space and 2x 2.4GHz CPU cores. It was actually pretty easy to install, actually quite a bit easier then I remember.

Installing GhostBin
  1. Install your operating system. I used Ubuntu 18.04 Server.

  2. Install Go:

    cd /usr/local
    wget https://dl.google.com/go/go1.12.8.linux-amd64.tar.gz
    tar -C /usr/local -xzf go1.12.8.linux-amd64.tar.gz

    Add to the bottom of your /etc/profile file with:

    export PATH=$PATH:/usr/local/go/bin

    You can either also run that at your command prompt or logout and log back in.

  3. Install Mercurial and Python Pygments:

    apt install mercurial python-pygments
  4. Install ansi2html:

    apt install python-pip
    pip install ansi2html
  5. Install Git:

    cd /usr/local/src
    apt install autoconf libssl-dev zlib1g-dev libcurl4-openssl-dev tcl-dev gettext
    wget https://github.com/git/git/archive/v2.22.1.tar.gz
    tar zxvf v2.22.1.tar.gz
    cd git-2.22.1/
    make configure
    ./configure --prefix=/usr
    make -j2
    make install
  6. I recommend creating a new user to run your GhostBin code under:

    adduser ghostbin
  7. You should also set a password on the new user account using passwd ghostbin.

  8. Login as your new user account and add the following to your ~/.bashrc file:

    export GOPATH=$HOME/go
  9. Save and exit the file and run source ~/.bashrc.

  10. Next obtain the source code for GhostBin (login as your new user first):

    mkdir -p ~/go/src
    cd $HOME/go/src
    mkdir github.com
    cd github.com
    git clone https://github.com/DHowett/spectre.git
    cd spectre/
  11. At this point your full path should be something like - /home/ghostbin/go/src/github.com/ghostbin.

  12. Run go get.

  13. Run go build.

  14. Run which pygmentize. It should return /usr/bin/pygmentize. If not, no problem, just copy the path.

  15. You'll also want to run which ansi2html which should return /usr/local/bin/ansi2html. Again, if it doesn't no big deal, just copy the path.

  16. Now exit the languages.yml file and update the path for pygmentize which should be on line 6. Also update the path for ansi2html which should be on line 23. Save and exit. Here's my languages.yml up to line 25 to give you an example:

        name: default
        func: commandFormatter
        - /usr/bin/pygmentize
        - "-f"
        - html
        - "-l"
        - "%LANG%"
        - "-O"
        - "nowrap=True,encoding=utf-8"
        name: text
        func: plainText
        name: markdown
        func: markdown
        name: ansi
        func: commandFormatter
        - /usr/local/bin/ansi2html
        - "--naked"
  17. Next we'll need to build a CSS file which will give color to the pastes:

    pygmentize -f html -S $STYLE > public/css/theme-pygments.css

    You can choose from several styles/color themes:

    - monokai
    - manni
    - rrt
    - perldoc
    - borland
    - colorful
    - default
    - murphy
    - vs
    - trac
    - tango
    - fruity
    - autumn
    - bw
    - emacs
    - vim
    - pastie
    - friendly
    - native

    I used monokai:

    pygmentize -f html -S monokai > public/css/theme-pygments.css
  18. If all went well all that is left to do is to start the service:

  19. Here's an screenshot of my install: GhostBin

  20. I would also recommend running the binary with the --help flag:

    $ ./spectre --help
    Usage of ./spectre:
      -addr string
            bind address and port (default "")
            log to standard error as well as files
      -log_backtrace_at value
            when logging hits line file:N, emit a stack trace
      -log_dir string
            If non-empty, write log files in this directory
            log to standard error instead of files
            rebuild all templates for each request
      -root string
            path to generated file storage (default "./")
      -stderrthreshold value
            logs at or above this threshold go to stderr
      -v value
            log level for V logs
      -vmodule value
            comma-separated list of pattern=N settings for file-filtered logging

    This allows you to see flags you can run with the binary. I run mine as such:

    ./spectre -logtostderr

    This will just log the errors to the screen.

Setting Up GhostBin w/ Nginx
  1. Install Nginx:

    apt install nginx
  2. Create a Nginx configuration file for GhostBin:

    nano /etc/nginx/sites-available/ghostbin.conf
    # Upstream configuration
    upstream ghostbin_upstream {  
         keepalive 64;
    # Public
    server {  
        listen 80;
        server_name ghostbin.YOURDOMAIN.com; # domain of my site
        location / {
            proxy_http_version 1.1;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
            proxy_set_header   X-NginX-Proxy    true;
            proxy_set_header   Host             $http_host;
            proxy_set_header   Upgrade          $http_upgrade;
            proxy_redirect     off;
            proxy_pass         http://ghostbin_upstream;

    You'll obviously want to update the server_name bit. Save and exit the file.

  3. Next we need to make a symlink so Nginx knows to load the configuration:

    cd ../site-enabled
    ln -s ../sites-available/ghostbin.conf .
  4. Restart the Nginx service:

    systemctl restart nginx

    Your GhostBin site should now be available at http://ghostbin.YOURDOMAIN.com!

Other Notes
  • Whenever I start up the binary I see:

    E0815 21:19:58.915843   19895 main.go:773] Expirator Error: open expiry.gob: no such file or directory

    This doesn't appear to be an issue and I haven't had any issues with using GhostBin thus far. There seems to be some code in main.go referencing it. It looks related to the expiration of the paste, but I don't know GoLang so I can't be sure.

    pasteExpirator = gotimeout.NewExpirator(filepath.Join(arguments.root, "expiry.gob"), &ExpiringPasteStore{pasteStore})
  • It looks like the GhostBin repository has been renamed to 'spectre'. It looks like this was done to "de-brand" it for people who want to run it themselves and separate it from GhostBin.com where I believe the developer run their own copy. See this commit.

  • You should definitely set up your install with Let's Encrypt for SSL.

2010 Mac Pro - High Sierra

Believe it or not the 2009 Mac Pro which outperforms some of the latest Mac models has been blacklisted from having macOS 10.13 High Sierra installed on it. Why? Who knows...But, with a little nerd skills you can get your 2009 Mac Pro updated to the latest version of macOS (which at the time of writing is 10.13 High Sierra).

I previously had gotten 10.12 installed which required a little hackery but was well worth it to get my system running on the latest version of macOS. High Sierra requires a firmware update so that your 2009 model is seen as a 2010 model. Thankfully this is relatively easy to do if you have some spare time on your hands and a little patience. Here's what you're going to need:

I initially had some trouble running the firmware patching tool on my Sierra installation so this I did some further digging and some other folks found that going from Mavericks would work fine. The error I was getting was 'Error 5570'. I utilized the following guide to make a bootable USB of macOS Mavericks. I had previously purchased a copy of Mavericks from the App Store so I just downloaded it using that method.

So once, you've gotten your bootable USB setup, get Mavericks installed into your spare hard drive. Then you'll want to grab the firmware patching tool, and the EFI updater from Apple. I am not positive why this was necessary, but I had to open/mount the EFI updater from Apple, then run the firmware patching tool. It's possible this post here explains why, but I am not sure if that's it. Anyways, get the EFI Updater mounted, then run the firmware patching tool. The only button on the firmware patcher that should be clickable is the 'Upgrade to 2010 Firmware'. Click that and the tool will do its thing. If all went well you should get a little informational popup telling you to shut your Mac Pro down. Follow through on that information. If that works out, you'll hear a long tone while starting up your system and the firmware will be updated!

2010 Mac Pro - High Sierra 2

You can open the System Profiler application and on the page it opens to, you'll see the firmware information. The Boot ROM Version should read 'MP51.007F.B03'. Great! Now shut down the system and put your original hard drive back in. Snag the free macOS 10.13 High Sierra installer from the App Store and run it. It may tell you an additional firmware update is needed. Follow through on that. Once booted back into macOS, you can visit the System Profiler and your Boot ROM Version should read, 'MP51.0084.B00'. The High Sierra installer application should also be already opened. Go ahead and install!

2010 Mac Pro - High Sierra 3

Once all is said and done and you're booted back to your desktop, you should have the latest firmware for the system and you'll be running at least macOS 10.13.2 High Sierra. I have yet to test out updates to the OS via the App Store, but if they've been anything like previous unsupported installs they'll work perfectly fine! Good luck!


In the fall of last year I was with one of my co-workers who brought up mechanical keyboards. I'd been somewhat familiar with them, but only at the Razer, Corsair, CoolerMaster level. He mentioned he had on order one Input Club's K-Type boards so I did a little looking into that and it definitely sounded cool. I almost wanted one myself however I learned it was a group buy and I wouldn't be able to get one, shucks!

I continued researching and learning everything I could about mechanical keyboards and the community. I learned all about different types of switches, brands, PCBs, soldering, keysets, keycap profiles, creating your own custom layouts and firmwares, and so much more. During all this time and in browsing over Reddit and Discord I came across one keyset which I immediately knew I wanted. It was SA Camping.

SA Camping 1

Unfortunately for me, this was a relatively rare set and generally high priced so I mostly just forgot about it and moved on. However, a week ago or so, a local, actually one of the guys who runs some of the keyboard meet ups posted on r/mechmarket his SA Camping set he was looking to sell. I thought about it for a few minutes, but decided, it's a set I've always dreamed of having, it's being sold right here in Houston, why not?!

SA Camping 2

Unfortunately in all my excitement I didn't really check out the keyset close enough. I've been sticking with 60% boards and more specifically the DZ60 PCBs with a 2U left shift. The SA Camping is a sculpted set and doesn't come with an R4 2U shift key. So I ended up getting a new DZ60 PCB and the 2.25U plate so I could put together a new build. Thankfully this still allows me to have arrow keys, however, in place of my usual 1U right shift, I have a 1.75U shift key (which I've actually just programmed to be a '/ ?' key.

My DZ60 with SA Camping

This build is something I consider my end-game. It includes the SA Camping keyset which I've always wanted, the DZ60 PCB (with directional arrow keys), and Kailh Box Jades. Using it has been awesome so far, I definitely enjoy the sculpted SA set along with the Box Jades. I'll likely post another picture when I put on the other novelty keycaps.

Alright so it's not an entirely new build, but I did update several components on my PC this past weekend. While I definitely loved the Fractal case I had, I wanted to get something even more beautiful and decided on the NZXT H700i case. I also updated my CPU cooler to an NZXT Kraken X62 along with 3x AER RGB 140mm fans for further cooling (and awesome looks)!

The Lich King

Pulling everything out of the old case was relatively simple, however, I may have overestimated the sizing of everything as I had some troubles putting everything back together. I believe the NZXT Kraken X52 would have sufficed with 120mm fans as well as the 3x 140mm AER RGB fans, likely would have been fine at 120mm instead. In fact, I am thinking about buying a 3 pack of the 120mm fans for the front of the case and using the other 2 140mm fans elsewhere. The Kraken X62 is also pretty snug and touches one of the RAM chips, hopefully, that does not cause any issues. The radiator itself is also quite snug and touches one of the heat sink covers on the motherboard, again, I hope this does not cause any issues. All in all, though, the H700i is an amazing case, looks wonderful and is the new home to the Lich King himself. This build runs much cooler than previously, in fact when at load, it still runs cooler than the previous build when it was idle. I'm definitely satisfied with this "small" update to my gaming PC!

One evening while I was working with my co-worker he brought up mechanical keyboards and the fact he was getting Input Club's K-Type soon. This prompted me to do some further research and ~6 months later, I've put together 10 or so custom built keyboards. I've learned a ton about the different pieces and parts that make up keyboards, what's involved in building them, and how to program them to use your own unique layouts. I've also learned that I love 60% keyboards. They have all the keys I need and none of the keys I don't.

The following is one of my recent builds. The parts used for this build are:

  • PCB: DZ60
  • Plate: 2U left shift
  • OEM Stabilizers
  • DSA Granite keycaps
  • Kailh Burnt Orange switches
  • 60% Transparent plastic case
  • 1.8mm LEDs
  • MiniUSB cable
  • Finish Line Extreme Fluoro Grease

Picture 1

While recommended to connect your PCB to your computer to test the keys I did not do that for this build. Should you so be inclined I recommend this website. You can use a paper clip to test each switch mount point. Thankfully ever PCB I've gotten has never had any issues, likely why I've been skipping this step for the last few builds.

The next step I take is lubing the stabilizers. This allows things to stay a bit quieter when you're hitting keys like the space bar, the shift keys, enter and backspace. For my build I only needed the stabilizers on the space bar, enter and left shift. Put the lube where the metal touches the plastic and only a little. Don't go crazy wild. In all honesty, I've likely used too much as seen in the image. However, I've yet to see any issues from this, and it's definitely kept things moving nicely.

I've also seen this happen too many times. Don't forget to put your stabilizers on your PCB!!! Too many times people forget these and solder in all their switches and realize it. This will require you to desolder everything. Not fun.

Picture 2

On this specific build, I wanted to use one LED for my artisan keycap. LEDs are quite easy to add. Of note, the longer leg is the positive side so make sure to put that one into the '+' hole. Here's the LED soldered in:

Picture 3

You'll see the keycap and LED in a little bit! It's really cool! Of note, normally I would add LEDs (if I was going to have them on the build) after the switches. However, since this build is using box switches, they need to go first, under the switch.

Alright, once I've gotten the stabilizers snapped into place, I place the plate over the PCB and put switches into the 4 corners. This helps me not only keep the plate in place but also lets me start mapping the switches and keys for the bottom row. Trust me, it's no fun desoldering keys because you've put them in the wrong places! You don't need to push the keycaps all the way down on the switches, just make sure they look right and are in the correct placement.

Picture 4

Next, once I've made sure I've got my bottom row switches in the right places, I will solder those in along with the 4 corners. Again, this just ensures the plate is kept in place and isn't moving all over the place while I add in the other switches. It just adds a little extra stability and will make life easier when finishing up the build.

Now with the corners and bottom row soldered in, I finish adding the rest of the switches to the remaining holes. I'll usually use some tweezers, under the plate, each arm on a side of the switch. I do this because it helps pop the switch in by holding the plate up. I can take a short video of this if anyone is interested. You can put all your remaining switches in now, or do a row, then solder, then a row, then solder until finished, it's completely up to you.

Picture 5

Make sure you ensure your switches are straight and aligned properly. You don't want crooked keys, it's annoying and doesn't look good.

Picture 6

Picture 7

Whichever you've decided to do, the basics are just popping your switches into plate and PCB and soldering them to the board. This step is likely the longest and more difficult. I will admit though, having never soldered before, it's very easy to pick up. If you're concerned you can find some old electronics or a broken PCB to try first.

Next, once my soldering is done I put my finished PCB into my case, and screw it in. Once that's secure I add my keycaps.

Picture 8

Picture 9

Picture 10

If you want a custom layout you can definitely do so. In my case, since the default firmware is for a Windows keyboard, I had to do a little swapping of the keys so it would work properly in a macOS environment. If anyone wants my Windows DZ60 or macOS DZ60 layouts, I'd be happy to update this post with them! I can also do a post on how to setup firmware and how to flash your board if there's any interest.

At this point, you should be all done and have your very own, hand-built custom keyboard!

As noted previously, I used an artisan keycap for my Esc key. It's an alien or Xenomorph if you may. It's the first brand new artisan keycap I've ever bought and well worth it. Unfortunately, my iPhone 7+ cannot capture LEDs properly, likely due to shutter speed and the light. But here's an image of the keycap up close.

Picture 11

So there you have it, a general overview of building your own custom keyboard. Find below some resources and product links. KBDFans, NovelKeys, and PimpMyKeyboard are my go to places for keyboards and parts, I highly recommend them!

Part Links

I absolutely love the new Hearthstone animated short. Blizzard is on point.

I'm a little late to the viewing party for this trailer, but I finally found a few spare minutes to give it my full attention, and wow. I was already quite excited for this game but after watching the latest trailer for Star Wars Battlefront II, I definitely can't wait to get my hands on it. The amount of detail and the quality of the environments and gameplay is incredible. One thing I did learn in from the trailer was that the single-player campaign was going to help bridge the gap between Return of the Jedi and The Force Awakens.

I also completely forgot about the beta which I should be getting access to since I put in a pre-order for the Elite Trooper Deluxe Edition. I believe I read that the beta was going to happen on October 4, which is next Wednesday. I'll have to re-up my Xbox Live Gold subscription, but I think it'll be well worth it! I'll definitely be streaming the beta as well as the final game when it comes out!


For whatever reason the other afternoon I thought it would be a good idea to dust off my Nintendo 3DS and finish up one of the games I had started many months ago. I finished up the game, but then I started poking around a little and started to look into updating the custom firmware I had installed. It looked like it was going to be a pretty quick update, however, that's never the case with my stuff. After doing a little bit Googling, I found this issue on GitHub. Same problem I was having. I then learned that I was pretty much light years behind on updates and I would need to do some extensive manual updating to get things going again.

Thankfully some wonderful folks have put together some really awesome guides which walked me through the whole thing. When I did get stuck, which was inevitable, I used their advertised Discord server to seek additional assistance. Within an hour or so I was back up and running on the latest version of the Luma3DS custom firmware.

I'm still working on understanding how all this works. Basically what I've been able to determine and what I believe on how this works is there's a bootloader that you must have installed. This allows for unsigned applications or code to run. During the boot process, you go from the bootloader, then to your custom firmware, which in my case is boot9strap as the bootloader, then into Luma3DS. Once you're in Luma3DS you can launch "homebrew" applications along with 3DS games. I could be completely wrong about that process, so feel free to leave a comment to correct me.

Unfortunately, since I was relying on EmuNAND and Menuhax I still have EmuNAND and that's where my games live. Fortunately, there's a guide available which walks you through the steps of moving over from EmuNAND to SysNAND (which is what the 3DS boots into by default. I'll be giving that a shot tonight likely. This way, I won't need to hold down the L button every time I boot up my 3DS.

Fingers crossed that process goes without issue! 🤞🏼


Wild West Online - Closed Alpha

Howdy partners!

The closed alpha of one of my highly anticipated games of the year finally dropped over the weekend. While I've only spent about an hours worth of time in the game, I believe that's about all the time one can really sink into the game at the moment. I've been invited into many alphas and betas before, and Wild West Online does not even feel like an alpha at this point. I would not hesitate to call it a pre-alpha. It's extremely unfinished and needs a lot of work before it gets into a beta state. The game is missing many components, and while this is completely understandable, there should have been one of two things done before they released it into a closed alpha.

  1. They could have much better set the expectations of what folks could and couldn't do in the closed alpha. Very little can actually be done in the world, before you simply get bored and log out. OR
  2. Polish it up a little more, and add in a short in-game tutorial on basic mechanics and add 1-3 quests so there was something to do besides wandering around aimlessly.

The world has little to no NPCs, very little environment to interact with, the environment that does exist has some untextured items (mostly found in houses), very little audio (they just barely add footstep sounds), somewhat forgettable control schemes, and lots of bugs (which are definitely to be expected). I also couldn't find anywhere to see how much money I have, I figured hitting the 'I' key which is noted as the inventory would do this, but hitting the key yield nothing.

Bugs I found include:

  1. There is a messed up horse in the starting area. I've found out that she's now known as Broke Neck Sally.
  2. You can just walk through certain doors.
  3. Calling for your horse can spawn him/her hundreds of meters away.
  4. You can get stuck on random and sometimes unseen objects, requiring you to log out and log back in.
  5. Walking on what appears to be dry land gives off the sound of walking in the water.
  6. Amount of money your characters has appears to change between logins (for example, one login I couldn't afford a drink at the bar, but could during another login)
  7. Lots of missing text in many of the menus.

I do however have hopes for the game and I very much wish that it will be a successful title. It's still very early in the development process, but I think a holiday 2017 delivery date is not achievable at this point. At least not for the depth and quality of a game I would expect. I really hope they take their time and do this right. The game has a lot of potential, though if all else fails it'll keep me occupied until Red Dead Redemption 2 comes out. 🤠