Setting Up WriteFreely on Debian 10

Since this is my first post I thought it would only make sense to write a tutorial on how to setup the WriteFreely web app which is what I'm using to host this blog. IMO, it is best to write documentation while it is still fresh in one's mind.

One goal of mine for this blog is to spread information on free software that empowers individuals. To me, WriteFreely is a great example of this. To be specific WriteFreely is a federated web application written in Go for hosting and writing blogs. The federated part is cool because links will integrate with other federated applications like Mastodon or Pleroma. Go is cool because it is light in resource usage so it is possible to run the app on a small server. This site is run on a 2G Digital Ocean droplet running Debian. I will be talking about how great Digital Ocean is throughout this post, but just to be clear, I am in no way affiliated, simply a satisfied customer.

Debian is the obvious choice for me, because it is my favorite Linux distro. Debian is one of the most successful free software projects ever, and it has a lot of history. The nice thing about Digital Ocean is they allow a variety of OS's to choose from, and they already have support for Debian 10 (buster) including great documentation that I used myself to get this site going.

WriteFreely is pretty neat, and their getting started guide is fairly useful, but because there are a few options on how to set it up the getting started guide leaves a bit to the imagination.

So this post will outline the specific steps I took to setup my instance. Without further ado this is how to get started with WriteFreely on Debian 10.

Setting up the Debian Server

Digital Ocean has a ton of good tutorial posts for different development purposes. Most of this post is extracting the relevant parts from other tutorials found around the web. I'll be listing the links per section, I highly recommend checking them out for further context and info.

ssh into root on the server, we use root to setup a user.

ssh root@*server_ip*

Create a new user:

adduser *username*

Set this new user as sudo:

usermod -aG sudo *username*

Now we have to setup the firewall.

apt update
apt install ufw

List the different applications:

ufw app list

Now we need to allow OpenSSH:

ufw allow OpenSSH

And next we need to enable it:

ufw enable

This means the firewall will block everything besides ssh connections.

The next thing we have to do is copy the SSH key used to access root into the current userspace.

cp -r ~/.ssh /home/*username*
chown -R *username*:*username* /home/*username*/.ssh

Now you can ssh with your new user account:

ssh *username*@*server_ip*

The rest of this post assumes you are accessing the server via this new user account just made.

Installing MySQL

WriteFreely gives the option of using two database backends. MySQL and Sqlite. I chose MySQL because it seems to be the default. This was interesting for me because I've never used MySQL before (typically I use Postgres). I sourced the relevant info for this section from this site:

There doesn't seem to be a package in the official Debian repository for MySQL, but MySQL as an organization does provide their own custom deb we can use.

We download the deb with wget, and use dpkg (the low level package tool for Debian) to manually install the deb package.

sudo apt install gnupg # Needed dependency
sudo dpkg -i mysql-apt-config_0.8.13-1_all.deb

Now we can use apt to finish the installation process. The difference between apt and dpkg, is that apt is functionally a high level wrapper around dpkg.

sudo apt update
sudo apt install mysql-server

This will ask you to setup a password and what version of mysql you want to use. I wasn't able to get version 8.0 to work with writefreely, so make sure to choose mysql version 5.

WARNING: Make sure to do this step correctly because it is very hard to undo. I was forced to destroy and reset my droplet because I couldn't figure out how to reverse this.

Now that the mysql server is installed we can access the mysql server like so:

mysql -u root -p

Here we create a new database:

CREATE DATABASE writefreely;

We also want to make a new user:

CREATE USER '*username*'@'localhost' IDENTIFIED BY '*password*';

Now we grant privileges to the new user:

GRANT ALL PRIVILEGES ON *.* TO '*username*'@'localhost';

Make sure to update the privileges:


Installing Nginx

Not going to Nginx kind of scares me, luckily Digital Ocean once again has a great tutorial, and WriteFreely provides an example config to use. So it was just a matter of connecting the dots.

The digital ocean tutorial has a lot of extra information that is helpful for general use cases, but not entirely relevant for what we are doing. So I'll outline the specific useful parts here.

Install nginx like so:

sudo apt install nginx

Easy. Now we have to get our environment all nice and tidy. We do this by setting up a server block.

First we create a new directory to host our files for the site:

sudo mkdir -p /var/www/*your_domain*/

Now we set permissions for this directory to our user account:

sudo chown -R $USER:$USER /var/www/*your_domain*/

This directory is where we will be installing the WriteFreely app later.

Now to configure the server block:

sudo nano /etc/nginx/sites-available/*your_domain*

Copy this config, and change the relevant details (most specifically the url):

server {
    listen 80;
    listen [::]:80;


    gzip on;
    gzip_min_length 256;
    gzip_comp_level 5;
    gzip_http_version 1.1;
    gzip_vary on;

    location ~ ^/.well-known/(webfinger|nodeinfo|host-meta) {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_redirect off;

    location ~ ^/(css|img|js|fonts)/ {
        root /var/www/;
        # Optionally cache these files in the browser:
        # expires 12M;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_redirect off;

We enable this server block by creating a symbolic link to the sites-enabled directory:

sudo ln -s /etc/nginx/sites-available/*your_domain* /etc/nginx/sites-enabled/

In the digital ocean tutorial, they also recommend uncommenting a value in etc/nginx/nginx.conf. However, this step may be optional. I did it anyways just to be safe.

Find a line that looks like this:

# server_names_hash_bucket_size 64;

Uncomment it. Then restart nginx:

sudo systemctl restart nginx

We are almost done with nginx configuration and one step closer to our beautiful blog. The last thing we have to do before setting up the writefreely app is securing nginx with lets encrypt.

Securing Nginx with Let's Encrypt

Once again, these steps are taken from a great tutorial created by Digital Ocean.

Install certbot like so:

sudo apt install python3-acme python3-certbot python3-mock python3-openssl python3-pkg-resources python3-pyparsing python3-zope.interface
sudo apt install python3-certbot-nginx

Certbot is ready to go, and we will use it setup our certificates and integrate them into nginx.

Before we do that, we first have to allow https traffic through the firewall.

sudo ufw allow 'Nginx Full'

Now we can obtain our certificate:

sudo certbot --nginx -d *your_domain*

If you want the www prefix for your site it is like this:

sudo certbot --nginx -d *your_domain* -d www.*your_domain*

That has a few config steps to go through and your set. We can test the auto renewal feature like this:

sudo certbot renew --dry-run

Installing WriteFreely

At last, the moment we've been waiting for. If you made it this far, good job, you're a trooper.

We begin by downloading the latest release of writefreely, which can be found here:

Copy the link location for the latest release and then use wget to download it to the server.

cd /var/www/*your_domain*
wget *link*
tar -xzvf *downloaded_tar_file*

Now you should see the writefreely binary. We use that binary to setup the config:

./writefreely --config

It will run through a few steps asking how you want it configured. Since we used Nginx select the option for production with reverse proxy. Make sure to set the database name, username, and password as the matching fields we used earlier.

I also selected it to be a single user blog, and I enabled federation. The rest of the defaults seem to work fine as well.

If this fails, it is most likely because you made a mistake with the MySQL configuration.

The next step is to setup the keys:

./writefreely --gen-keys

Now we are almost ready to fire up the website. Before we do that, we want to make the writefreely binary a systemd service. WriteFreely provides this great example config which should be placed at /etc/systemd/system/writefreely.service:

Description=WriteFreely Instance



We kickstart the service:

sudo systemctl start writefreely

Verify that everything is working correctly:

sudo journalctl -f -u writefreely

If there are no errors then you did it!

Now navigate to your site with the browser, and voila! You have a self hosted, free software, federated blog running on Debian. I don't know about you, but that has got me excited.


Hopefully someone in the world finds this useful. I certainly would've liked to have been spoonfed these instructions. This was quite the technical tutorial, but it is my intent to post about different sorts of things. Probably mostly about tech, but hey, that's what I know!

This is fancycade, signing off.