[] Dark Mode

Self-Hosting GNU Social

(posted in tech)

Out of Date Notice (Nov. 2019): I no longer use GNU Social or Mastodon or any social networking software of any kind. The details in this article are probably very outdated.

I remember telling a friend about Facebook back when MySpace was still the most popular social network around. I explained how it was similar, in that you could connect and share messages, links, and photos with your friends and family, but—unlike MySpace—the interface was professional and clean. Everyone who used Facebook saw basically the same thing; you wouldn’t run into any profile pages with animated backgrounds, auto-playing MP3 music, and comically unreadable font color choices.

“You can’t make it your own?” she asked, head cocked slightly to one side. “Why would anyone want to use that?”

Why Indeed?

I was relatively passive on Facebook for a while, using it primarily to see what was going on in the lives of friends, family, and acquaintances I had left behind when I moved to the US from Canada. As privacy and ownership of my information on the Internet slowly became bigger concerns to me, I used social networks less and less, eventually culminating in the deletion of my accounts entirely.

There are lots of reasons not to use Facebook. I have less of a problem with Twitter from a privacy and ownership standpoint, but their open hostility toward 3rd party developers, forcing all links to go through their t.co link tracking service, and general movement toward becoming a media and advertising company instead of the open communications platform it has the potential to be have dampened my desire to use it.

There are an ever-increasing number of stories about people who grew up in a religion, only to become atheists later in life. The thing they often miss the most is the feeling of community that church attendance and other religious social ceremonies used to provide them. Deleting all of your social network accounts can leave you in a similar position—with a social void to fill. How can a lonely privacy nerd fill that void?

Enter the Decentralized Web

There are actually a lot of projects out there that aim to recreate the experience of Facebook or Twitter in a decentralized fashion.

Diaspora raised over $200,000 during its 2010 Kickstarter, but failed to materialize in the way everyone hoped. It’s still around today in some form, but it’s not exactly clear to me what that form actually is. Their splash page (at the time of this writing) has ten different sections with “Find Out More” buttons talking about “pods” and “aspects” and other confusing things that clearly don’t involve downloading, installing, and hooking my own instance up to a larger federated social network.

Pump.io is another project, created by Evan Prodromou, who previously created StatusNet. This seems like a definite contender, although its future may be in question as Evan recently stated that he can no longer maintain and continue its development. Hopefully the changes that come about due to the changes in its ownership and development structure allow it to continue to mature as a platform.

Tent.io sounds cool from the description on their page, but I can’t find any examples of what it actually looks like. They don’t link to existing hosts, and the one that I did manage to find (Cupcake) just has a button to sign up and that’s about it—no public or example activity streams to look at. It may be a viable solution if you’re willing to give it a go, but I didn’t want to spend any time on it without being able to easily tell what I was getting into.

GNU Social (previously known as StatusNet) seems to be, as of right now, the most mature and convenient project in the decentralized social networking market. At the top of the site you’ll find both a download link to its git repository and a “Try It!” link that brings you to a directory of open public instances that you can sign up for and use right away. It’s the one I opted for (which I’m running at status.rudism.com, and the one that I’m focusing on in this article (in case that wasn’t already apparent from the title).

Holy Cow, Enough Preamble Already!

In my case, I set up GNU Social on my DigitalOcean (affiliate link) server which is running Centos 7. If you’re using Ubuntu or some other distro you’ll have to figure out the equivalent commands on your own (apt-get instead of yum, for example). I’ll assume that if you’re the type of person who’s interested in running your own GNU Social instance, you probably already have a pretty good grasp of the command line and how to use your preferred flavor of Linux.


It’s probably a good idea to at least gloss over the official installation docs before getting started (something may have changed between the time I wrote this article and whenever you’re reading it).

The first prerequisite (PHP 5.5+) is already a problem on Centos, because if you install the official php package using yum install php, you’ll end up with version 5.4 installed. I solved this by installing the Remi repository:

$ sudo yum install http://rpms.famillecollet.com/enterprise/remi-release-7.rpm

After running that command, as root edit the file /etc/yum.repos.d/remi.repo, find the section near the top labelled [remi] and change the enabled=0 line under it to be enabled=1, and then do the same thing under the [remi-php56] section. Then save that file and do an update:

$ sudo yum update

If your system already had PHP 5.4 installed, that will update it to 5.6. Now let’s install all of the prerequisites. I’m including some stuff that you might already have installed for completeness’ sake, as well as Nginx and php-fpm (which aren’t necessary if you want to diverge from this guide and use Apache instead):

$ sudo yum install php php-fpm nginx mariadb-server php-mbstring php-curl php-gd php-gmp php-intl php-json php-mysqlnd php-pecl-zendopcache libexif git

Once that’s all done, it’s time to run and set up your database:

$ sudo systemctl enable mariadb
$ sudo systemctl start mariadb
$ sudo mysql_secure_installation

Go through the secure installation thingy, setting a good root password and so on.

Get GNU Social

Check out the source code from git:

$ git clone https://git.gnu.io/gnu/gnu-social.git
$ cd gnu-social

I personally use the nightly branch because I’m just a bleeding-edge junky who flies by the seat of his pants and damns all the consequences. If you are also a super awesome dude like me, you can optionally do this, you crazy rebel:

$ git checkout nightly

Create the Database

Let’s create a database for GNU Social to use now.

$ mysql -u root -p
Enter password: *****
> GRANT ALL ON social.* TO 'social'@'localhost' IDENTIFIED BY 'supersecretpassword';
> \q

You should probably use a better password than supersecretpassword on that last command. Totally up to you, though.

Configure the Webserver

As I stated earlier, my preferred method of running PHP applications is using Nginx and php-fpm, so that’s what you’re getting here. You could also run it using Apache or any other PHP-capable web server of your choice.

First thing I like to do is change php-fpm to listen on a unix socket instead of a TCP socket (because only local processes will be connecting to it anyway). To do that, edit the /etc/php-fpm.d/www.conf file and change the existing listen directive like so:

# listen =
listen = /var/run/php-fpm/php-fpm.sock
listen.owner = nginx
listen.group = nginx
listen.mode = 0666

Also find the user and group directives and change them to nginx:

user = nginx
group = nginx

There is another setting in this file you should consider changing, especially if you are just going to be running a small or single-user instance:

# pm = dynamic
pm = ondemand

By changing pm to ondemand instead of dynamic, you will vastly reduce the resources used by php-fpm—it will only spin up new listeners as they are needed instead of keeping an always-ready pool of listeners running at all times.

Now we can enable and run the php-fpm service.

$ sudo systemctl enable php-fpm
$ sudo systemctl start php-fpm

The next piece is to configure Nginx. Create a new file at /etc/nginx/conf.d/gnu-social.conf that looks like this:

server {
  listen 80;
  server_name your.domain.com;
  root /path/to/gnu-social;

  location ~ \.php$ {
    fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /path/to/gnu-social/$fastcgi_script_name;
    include fastcgi_params;

  location / {
    index index.php;
    if (!-e $request_filename) {
      rewrite ^(.*)$ /index.php?p=$1 last;

Be sure to replace your.domain.com with your actual domain name, and both instances of /path/to/gnu-social with the actual path to where you checked out the source code using git.

You now need to grant Nginx and php-fpm (both of which run using the nginx user and group) the ability to read and write to specific directories in your gnu-social path. Probably the best way to do this is to change the group permissions on those directories:

$ cd /path/to/gnu-social
$ sudo chgrp nginx .
$ chmod g+w .
$ mkdir avatar background file
$ sudo chgrp nginx avatar background file
$ chmod g+w avatar background file

It’s important to ensure that the nginx user or group will have read access to your gnu-social directory all the way up your directory tree. If, for example, you installed gnu-social into your home directory, chances are that your home directory will not be world-readable. You could move the gnu-social directory somewhere else such as somewhere under /var/www, or simply enable read access to your home directory if you’re not concerned about the security implications of doing that:

$ chmod a+rx /home/yourusername

Now we can enable and start Nginx.

$ sudo systemctl enable nginx
$ sudo systemctl start nginx

Install and Configure GNU Social

Now you just navigate to the install script on the domain you configured in your gnu-social.conf Nginx configuration, for example http://your.domain.com/install.php. If it tells you there are problems (for example, missing PHP extensions or unwritable directories) you can fix them at this point and try again until you are presented with the GNU Social configuration form.

Choose a site name, enable Fancy URLs, disable SSL (I leave it as an exercise for the reader to set up SSL), choose your username, password, site type (single user, community, public, etc.), and enter your database details (using the password you set earlier in the GRANT ALL sql command):

Hostname: localhost
Name: social
User: social
Password: supersecretpassword

If, after you submit the form, you see an error like this:

Fatal error: Can't use method return value in write context in /home/vagrant/gnu-social/classes/Profile.php on line 147

It means you’re still running an old version of PHP. On your server, if you run php --version you’ll probably see something like this:

$ php --version
PHP 5.4.16 (cli) (built: Jun 23 2015 21:17:27)

But you want to see this:

$ php --version
PHP 5.6.13 (cli) (built: Sep  3 2015 14:08:58)
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies

You’ll have to go back to the steps around installing and enabling the remi repositories and see if you can figure out where it messed up. The goal is to get your PHP version upgraded to version 5.5 or higher.

Otherwise, you’ll get a bunch of output that includes the following phrase at the end:

DONE! You can visit your new GNU social site...

You might get a bunch of Deprecated warnings too, but as long as they’re just warnings and not errors that prevented the process from completing you’re fine. Now you can visit your domain directly and log into your brand new GNU Social instance!

There is another possible gotcha here. If you log in and are confident that you entered your login and password correctly, but are presented with the following error message:

There was a problem with your session token.

It probably means that your php session directory is non-writable by the nginx user or group. I ran into this problem on my setup and was able to fix it like this:

$ sudo chgrp -R nginx /var/lib/php/session /var/lib/php/wsdlcache
$ sudo systemctl restart php-fpm
$ sudo systemctl restart nginx

Start Getting Your Social On

If you successfully made it this far, you’re already good to go. You can start searching some of the other public GNU Social instances like Load Average, Quitter, or (my favorite) The Rainbow Dash Network, to find cool and interesting people to follow. You can branch out to other self-hosted instances by keeping an eye out for unfamiliar @names in status updates or by checking out !group pages on the public instances.

If you know how to Twitter, this part should come easy to you.

One thing it is worth mentioning—on Twitter if you decide you don’t want there to be a record of that drunken tweet you posted last night, you can log into your account and delete it and the tweet will disappear from all your followers’ timelines (meaning the only remaining evidence that it ever existed are the screenshots that your buddies took in order to blackmail you later). You can’t really do that on a decentralized system like GNU Social. You can delete the status update from your own timeline and instance, but if it had already propagated to your followers on other instances, those copies of the status update are out there forever.

Updating GNU Social

I don’t know if this stuff is actually documented anywhere… I just kind of picked it up from vague status updates I read from other people in the “fediverse” (that’s the name for all of the federated GNU Social instances out there, which you are now a member of if you’ve made it this far). Here’s what I do when I want to update to the latest version of the code:

$ cd /path/to/gnu-social
$ git pull
$ cd scripts
$ php upgrade.php

That’s worked for me a couple times so far. If there are other necessary steps then I don’t know what they are.

Update 2015-10-10: It was pointed out to me that it’s probably a good idea to stop any daemons you have running before the upgrade by running scripts/stopdaemons.sh, then start them up again afterwards using scripts/startdaemons.sh.

At one point I updated my site to use SSL after I had already been running it as a non-SSL site for a while. It was mostly a matter of buying the SSL certs, adding the secure configuration to my Nginx gnu-social.conf file, and then changing and/or adding lines in gnu-social/config.php to this:

$config['site']['ssl'] = 'always';
$config['avatar']['ssl'] = true;

This still resulted in mixed content errors on the site because a bunch of non-SSL avatar urls had been saved to my database. I was able to fix that with the following SQL query:

$ mysql -u social -p social
> UPDATE avatar SET url=REPLACE(url, 'http://', 'https://');

I think there was something in the gnu-social/scripts directory that sounded like it should have done that for me, but I couldn’t get anything to work other than running that query directly.


You can check out the gnu-social/plugins directory to see if there’s anything cool in there you want to enable. Most of them either have a README file or comments in the php scripts that describe how to use them. For example, I enabled the LilUrl plugin on my instance to use my rdsm.ca url-shortener by adding this line to my gnu-social/config.php file:

addPlugin('LilUrl', array('shortenerName'=>'rdsm.ca','freeService'=>true,'serviceUrl'=>'http://rdsm.ca/'));

Then, when logged into my GNU Social instance, I went to Settings->URL and was able to select rdsm.ca from the “Shorten URLs With” dropdown.


If you don’t like the default, generic GNU Social theme, you can look in the gnu-social/theme directory to see what’s available, and switch the theme of your site by adding a line like this to your gnu-social/config.php file:

$config['site']['theme'] = 'neo-quitter';

I really like the neo-quitter theme, but found a few things about it that bugged me (some broken styles, weird margin issues, stuff like that) so I forked it and have committed some of my minor tweaks and fixes on Github. If you feel like trying it out you’d do something like this:

$ cd /path/to/gnu-social/theme
$ git clone https://github.com/rudism/neo-quitter.git neo-rudism

And then add the config line I mentioned to set the theme to neo-rudism instead of neo-quitter. There might be other themes out there on Github that are worth checking out too, I didn’t really look around too much.

More Scripts

I don’t know what most of the stuff in gnu-social/scripts is for, but you can fish around in there and see if there’s anything you might want to use. The one I did set up is the queuedaemon.php script, which as I understand it can maybe speed up your instance and improve the reliability of your status updates getting out to your followers (because there is a dedicated daemon consuming the item queue instead of relying on asynchronous processes spawned by the web server). To do that, I added the following lines to my gnu-social/config.php file:

$config['queue']['enabled'] = true;
$config['queue']['subsystem'] = 'db';
$config['queue']['daemon'] = true;

And then ran this command:

$ cd /path/to/gnu-social/scripts
$ sh startdaemons.sh

You should see a message that queuedaemon.php was started. And you should see queuedaemon.php as running processes on the machine after that:

$ ps -ef | grep queuedaemon
user   1234     1  0 15:06 pts/1    00:00:00 php /path/to/gnu-social/scripts/queuedaemon.php
user   1235  4321  0 15:06 pts/1    00:00:00 php /path/to/gnu-social/scripts/queuedaemon.php
user   1236  4321  0 15:06 pts/1    00:00:00 php /path/to/gnu-social/scripts/queuedaemon.php

To verify that it’s working, you should also see QueueDaemon related entries in your system logs (though it might take a while for these to show up if there’s very little activity going on in your GNU Social instance):

$ journalctl | grep queuedaemon
Sep 27 15:06:04 hostname statusnet[4321]: [your.domain.com:queuedaemon.php:4321] QueueDaemon (generic): Spawned thread 1 as pid 1234
...checking for queued notices
...Waiting for children to complete.
...claiming queue item id = 1234 for transport distrib
...claim succeeded.
...Got Notice 4321 for transport distrib
...OStatusPlugin: Notice 4321 queued for OStatus processing
...DBQueueManager (main): [distrib:Notice 4321] Successfully handled item

I don’t really know what any of that means but it sure sounds like it’s successfully doing something. My site is also still working, so that’s a promising sign. To start the queuedaemon at boot time, I added this entry to my crontab (you can edit your crontab with crontab -e):

@reboot /bin/sh /path/to/gnu-social/scripts/startdaemonts.sh >/dev/null

I also run Monit on my server and configured it to watch the queuedaemon’s pid file at /tmp/queuedaemon.generic.pid so that it can restart it if it ever stops for some reason (which it never has in the last week or so that I’ve been running it).

Mobile Clients

It looks like there are some plugins and scripts to enable XMPP and SMS/Email support in GNU Social, which could be possible avenues to interact with it from an iPhone or Android, but they’re not something I’ve messed with myself. Instead I use the free AndStatus app for Android. Any microblogging client that has support for GNU Social and/or StatusNet will work with your self-hosted instance.

That’s It

That’s everything I know about how to run your own GNU Social instance on Centos 7. Use your newfound knowledge wisely.

Short Permalink for Attribution: rdsm.ca/t2hyd