Build WriteFreely from source

WriteFreely is open source, lightweight blogging platform written in Go programming language.

Motivation to build WriteFreely

While absolute king of blogs is WordPress, which in fact powers somewhat 30% of all websites, some say it is overloaded, choosing perfect theme is extremely difficult and some (especially free) plugins are poisoned with different kinds of tracking. I do use WordPress on my main site as well, but over the years I' ve figured it seriously takes time to post an article (and I'm not yet even aquainted with Gutenberg).

WriteFreely here is a nice alternative claiming to offer a distraction-free writing experience. While supporting HTML, software mainly relies and endorses markdown. I do not plan ditching WordPress anytime soon, but I was very curious to try out lightweight platform that natively federates.


This guide details how to build decent version of WriteFreely from source on CentOS 7 machine.

Instructions should work for other Linux distributions as well but bear in mind that some paths and details (yum vs. apt, etc.) might differ across systems.


Version 1.10+ is required. Download from Go website or (preferably) install using package manager:

yum install golang go-bindata

At the time of writing sufficient version was available on standard repository:

go version go1.11.5 linux/amd64


Make sure EPEL repository is installed:

yum install epel-release

Install Node.js and package manager npm:

yum install nodejs npm


Less.js (source) is needed to convert Less stylesheets to CSS.

npm install -g less
/usr/bin/lessc -> /usr/lib/node_modules/less/bin/lessc
- ycssmin@1.0.1 node_modules/less/node_modules/ycssmin
└── less@3.9.0 

npm install -g less-plugin-clean-css
└── less-plugin-clean-css@1.5.1

From above output make sure to remember /usr/bin/lessc – we will need it later.

MySQL or MariaDB

WriteFreely requires MySQL (MariaDB) 5.6+. This can be an issue with CentOS / RHEL as at the time of writing it was shipping version 5.5 which fails database initialization script on tables accesstokens, appcontent, posts and users. See my comment on issue #22.

Possible solution would be adding official MariaDB repository and installing decent version (v. 10 at the time of writing).

Create system user to run WriteFreely

For security reasons it is well worth creating unprivileged system user to run Write freely. Create new user executing:

adduser writefreely

Up to this point we've been using root or sudo user to install prerequisites. Below commands should be run under unprivileged user. Switch to it by running:

su - writefreely

Make sure user has following paths in ~/.bash_profile:

export GOBIN="$HOME/go/bin"
export GOPATH="$HOME/go/src"

If not, add them and propagate those changes:

source /etc/profile && source ~/.bash_profile

Ready to start building

ArchLinux has WriteFreely packaged – page provides good summary of dependencies. Anyhow at this point you should be good to go with prerequisites to start building.

Download WriteFreely sources

Pull sources from GitHub:

go get -d -u -v

Parameters explained below:

-d - do not build after downloading
-u - update
-v - verbose for details

Sources of WriteFreely and included libraries should now be available at ~/go/src/src/ into this folder.

Starting version 0.9 it is needed to explicitly tell to build with modules:

# If running Go 1.11+
export GO111MODULE=on

Otherwise such error would come up with next step:

../../sitemap.go:23:22: not enough arguments in call to stm.NewSitemap
	have ()
	want (int)
../../sitemap.go:78:4: index must be non-negative integer constant
../../sitemap.go:78:4: cannot use p.Post.Slug.NullString.String (type string) as type []interface {} in array or slice literal
../../sitemap.go:79:4: index must be non-negative integer constant
../../sitemap.go:79:18: cannot use "weekly" (type string) as type []interface {} in array or slice literal
../../sitemap.go:80:4: index must be non-negative integer constant
../../sitemap.go:80:4: cannot use true (type bool) as type []interface {} in array or slice literal
../../sitemap.go:81:4: index must be non-negative integer constant
../../sitemap.go:81:4: cannot use p.Post.Updated (type time.Time) as type []interface {} in array or slice literal
../../sitemap.go:86:33: index must be non-negative integer constant
../../sitemap.go:86:33: too many errors
make: *** [build] Error 2

Go ahead and make:

make build   # Compile the application

At this point writefreely executable should be created at ~/go/src/src/ Don't cd into that directory directly but run config.ini creation wizard:

./cmd/writefreely/writefreely --config # Create configuration file

Above step is not needed if you are updating from previous version – in such case keep config.ini file from ~/go/src/src/!

If doing fresh install, initialize database:

# (if you chose Multi-user setup) Import the schema with:
./cmd/writefreely/writefreely --init-db

Or upgrade if was running WriteFreely before:

./cmd/writefreely/writefreely  --migrate

Proceed with installation:

make install # Generates encryption keys; installs LESS compiler

At this point we need to generate CSS files. Problem is that LESS compiler Makefile needs little commenting – edit file less/Makefile leaving lessc path:

# ifeq ($(shell which lessc),/usr/bin/lessc)
# else
#	LESSC=node_modules/.bin/lessc

I didn't have time to fix script yet, but on my system make ui was referring to node_modules/.bin/lessc instead of /usr/bin/lessc therefore error was thrown. So with modification it is good to go:

make ui # Generates CSS (run this whenever you update your styles)

Update on lessc issue: I have filled a pull request to include /bin/lessc to less/Makefile. When it is accepted, above manual fix will not be needed.

And to check if application runs:

make run     # Runs the application

If application is running, quit it by hitting CTRL+C and set up system service. Before that just make sure writefreely binary appears at ~/go/bin folder. Command make run should have placed it there already but if not, copy binary from cmd/writefreely.

Install system service

Define system service in file /etc/systemd/system/writefreely.service:

Description=Write Freely Instance mariadb.service

; run as writefreely user


Pay attention – service should run under unprivileged user!

Now service can be started:

systemctl start writefreely

And enable service to start on next system restart:

systemctl enable writefreely

Setup reverse proxy (Nginx)

Nginx texmplate from WriteFreely guide should be good to go:

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;

This is default for unsecure connection on port 80. I recomment setting up WriteFreely on secure server block and redirect port 80 connections (both with and without www.) as well as secure connection with www. to straightforward instance domain – in my case it is


If SELinux is enabled in your CentOS / RHEL installation (default), below error will pop up in Nginx error log:

2019/03/02 01:19:46 [crit] 4508#0: *1 connect() to failed (13: Permission denied) while connecting to upstream, client:, server:, request: "GET / HTTP/1.1", upstream: "",host: ""

In /var/log/audit/audit.log this message will follow:

type=AVC msg=audit(1551482556.752:6031): avc:  denied  { name_connect } for  pid=4576 comm="nginx" dest=8080 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:http_cache_port_t:s0 tclass=tcp_socket permissive=0

Create an exception by running:

setsebool httpd_can_network_connect on -P

That's it, you have successfully built and launched WriteFreely on CentOS :)

My fork

I do have a fork of WriteFreely on GitHub for pull request purposes. While there is no good reason you might clone from my repository instead of official one, for the reference here it is:

go get -d -u -v

Notice I use as placeholder. It is nothing but static website with few meta tags to tell go get where to pull code from. In essence it actually points to my repository on GitHub, but just in case I want to switch to (most likely) on-premises Gitea, I won't need to update this documentation. Sources are pulled to following folder then:


Also I use branch fedi-dev – on it at the moment I have removed hotlinked video from landing page. So if using my repository, make sure to switch to this branch:

git checkout fedi-dev

And start building from here.

Further reading


Comments or any feedback? Toot me or contact. My all fediverse handles here.


Tags: #server #go #golang #less #build #writefreely #nginx