Mouflons and Penguins

writefreely

I have been using WordPress ever since I decided to have a personal website/blog. It has served me well but I've come to a point where I got tired of messing with plugin conflicts, and spammers. I also like Markdown a lot and the available plugins for WordPress were either broken or had conflicts with other plugins. So I decided to try something simpler. At first I had my mind on static generators but then I came across WriteFreely, a minimalistic, distributed and customizable blogging software. It supports Markdown and HTML and that's all I need right now. On the minus side, it does not support media uploads but we will show a workaround to that, on this guide.

Prerequisites

  • A Debian buster system (Ubuntu or other Debian derivatives should work too.)
  • Install dependencies:
# apt install nginx mariadb-server certbot python3-certbot-nginx
  • Some domains like example.org, www.example.org and media.example.org:
www.example.org.	300	IN	CNAME	example.org.
media.example.org.	300	IN	CNAME	example.org.
example.org.		300	IN	A	1.2.3.4
  • Remove skip-name-resolve from /etc/mysql/mariadb.conf.d/50-server.cnf and restart MariaDB [2]:
$ sudo sed -i -r -e 's/(skip-name-resolve)/#\1/' /etc/mysql/mariadb.conf.d/50-server.cnf && \
sudo systemctl restart mariadb

Setup WriteFreely

  • Create a writefreely system user:
$ sudo useradd -r -m -d /srv/writefreely writefreely
  • Download and extract WriteFreely:
$ sudo su - writefreely
$ wget https://github.com/writeas/writefreely/releases/download/v0.12.0/writefreely_0.12.0_linux_amd64.tar.gz
$ tar xvzf writefreely_0.12.0_linux_amd64.tar.gz
$ mv writefreely example.org
  • Prepare the database:
$ sudo mysql
MariaDB [(none)]> CREATE DATABASE writefreely;
MariaDB [(none)]> GRANT ALL PRIVILEGES ON writefreely.* TO 'writefreelyuser'@'localhost' IDENTIFIED BY 'MySecretDBPass';
  • Generate the config file:
$ ./writefreely --create-config
  • Apply the following changes in the generated config.ini file:
--- config.ini.orig     2020-08-08 13:57:13.377920751 +0300
+++ config.ini.new      2020-08-08 14:56:48.032964841 +0300
@@ -14,16 +14,16 @@
 [database]
 type     = mysql
 filename = 
-username = 
-password = 
-database = 
+username = writefreelyuser
+password = MySecretDBPass
+database = writefreely
 host     = localhost
 port     = 3306
 
 [app]
-site_name          = 
-site_description   = 
-host               = http://localhost:8080
+site_name          = Roasted Chickpeas Downhill
+site_description   = Roll those Chickpeas!
+host               = https://example.org
 theme              = write
 editor             = 
 disable_js         = false
@@ -43,7 +43,7 @@
 private            = false
 local_timeline     = false
 user_invites       = 
-default_visibility = 
+default_visibility = unlisted
 update_checks      = false
 
 [oauth.slack]

If you prefer a multiuser site you can change the single_user directive to false. You can also choose how many blogs per user with max_blogs. A full list of list of configuration options from the WriteFreely config documentation page.

NOTE: I noticed that .using ./writefreely config start hungs at some steps so you may want to avoid that until the problem is resolved.

  • Initialize the database:
$ ./writefreely db init
  • Generate encryption keys:
$ ./writefreely keys generate
  • Create an admin user:
$ ./writefreely --create-admin admin:MySecretAdminPass 

Create a WriteFreely service:

Create a systemd service file (/etc/systemd/system/writefreely.service):

[Unit]
Description=WriteFreely Instance
After=syslog.target network.target mysql.service

[Service]
Type=simple
StandardOutput=syslog
StandardError=syslog
User=writefreely
Group=writefreely
WorkingDirectory=/srv/writefreely/example.org
ExecStart=/srv/writefreely/example.org/writefreely
Restart=always

[Install]
WantedBy=multi-user.target
  • Enable and start the service:
$ systemctl daemon-reload 
$ systemctl enable --now writefreely.service
  • Verify :
# systemctl status writefreely.service ; echo; ss -lnptu | grep writefreely
● writefreely.service - WriteFreely Instance
   Loaded: loaded (/etc/systemd/system/writefreely.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2020-08-02 11:18:43 EEST; 46s ago
 Main PID: 20540 (writefreely)
    Tasks: 16 (limit: 4915)
   Memory: 18.3M
   CGroup: /system.slice/writefreely.service
           └─20540 /srv/writefreely/freely.theo-andreou.org/writefreely

Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Loading config.ini configuration...
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Loading templates...
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Loading pages...
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Loading user pages...
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Loading encryption keys...
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Connecting to mysql database...
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Adding {domain} routes (single user)...
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Going to serve...
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 Serving on http://localhost:8080
Aug 02 11:18:43 galadriel writefreely[20540]: 2020/08/02 11:18:43 ---

tcp     LISTEN   0        128            127.0.0.1:8080          0.0.0.0:\*       users:(("writefreely",pid=20540,fd=5))

Setup the Reverse Proxy

  • Prepare an Nginx virtualhost file (/etc/nginx/site-available/example.org):
server {
    listen 80;
    listen [::]:80;

    server_name example.org www.example.org;

    gzip on;
    gzip_types
      application/javascript
      application/x-javascript
      application/json
      application/rss+xml
      application/xml
      image/svg+xml
      image/x-icon
      application/vnd.ms-fontobject
      application/font-sfnt
      text/css
      text/plain;
    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_pass http://127.0.0.1:8080;
        proxy_redirect off;
    }

    location ~ ^/(css|img|js|fonts)/ {
        root /srv/writefreely/example.org/static;
        # 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_pass http://127.0.0.1:8080;
        proxy_redirect off;
    }
}
  • Enable the virtualhost:
$ cd /etc/nginx/sites-enabled/
$ sudo  ln -s ../sites-available/example.org
$ sudo nginx -t && sudo systemctl reload nginx
  • Enable TLS on the virtualhost:
# certbot run --nginx --agree-tos -m me@example.org --redirect -n -d example.org

Now visit https://example.org to use your newlly created WriteFreely Blog

Customizing the Look and Feel

If you want to change the way your blog looks you can use the Customize option after you login. Under Custom CSS you can use add you own CCS. Mine is based on the Dark Theme with some additional twaeks. Feel free to use it as you wish:

/* 
   Written in 2020 by Theodotos Andreou (based on the Dark Theme by Write.as)

   To the extent possible under law, the author(s) have dedicated all 
   copyright and related and neighboring rights to this software to the 
   public domain worldwide. This software is distributed without any 
   warranty.

   You should have received a copy of the CC0 Public Domain Dedication 
   along with this software. If not, see 
   http://creativecommons.org/publicdomain/zero/1.0
*/

body {
		 color: #eee;
		 background: #111;
	}
	
body .post-title a:link, a:visited {
		 color: #ccc;
	}

#collection #wrapper pre {
         background: #393e46;
}

#collection #wrapper code {
		 background: #393e46;
	}

#collection code {
		 background-color: #393e46;
	}

#collection .post-title a {
               color: #ccc;
}
	
#collection {
		 color: #eee;
	}
	
body h1 a, body header h2 a {
		 color: #cfcfcf;
	}
	
body h1 a:hover, body header h2 a:hover {
		 color: #fff;
	}
	
body h1 a:visited, body header h2 a:visited {
		 color: #ccc;
	}
	
body #manage ul a, body .dropdown-nav ul a, body #manage ul ul a, body #manage ul a {
		 color: #ccc;
	}
	 
body .dropdown-nav ul ul, body .dropdown-nav ul li:hover, body #manage ul ul, body #manage ul ul li:hover, body #manage ul li:hover {
		 background: #222;
	}

body #official-writing h2, body #official-writing h3, body #official-writing h4, body #wrapper h2, body #wrapper h3, body #wrapper h4 {
		 color: #ddd;
	}
	
body a {
		 color: #f6fccd;
	}
	
body a:visited {
		 color: #f6fcb0;
	}
	
body #official-writing h2, body #official-writing h3, body #official-writing h4, body #wrapper h2, body #wrapper h3, body #wrapper h4 {
		 color: #aaa;
	}
	
body #official-writing ul.collections li.collection a.title:link, body #official-writing ul.collections li.collection a.title:visited, body #wrapper ul.collections li.collection a.title:link, body #wrapper ul.collections li.collection a.title:visited {
		 color: #cfcfcf;
	}
	 
body #official-writing ul.collections li a.create, body #wrapper ul.collections li a.create {
		 color: #aaa;
	}
	 
body#me #official-writing h2 a:link, body#me #official-writing h2 a:visited {
		 color: #a2a2ff;
	}

header p.description {
         color: #fcfcfc;
    }

header .pinned {
         color: #f6fccd;
    }

body p.code {
         background: #f6fcb0;
    }


body header nav a:visited {
         color: #f6fcb0;
    }

body header article code {
         background: #ff0000;
    }

body pre {
         background: #f6fcb0;
    }

body footer nav {
         color: #fcfcfc;
    }

input {
		 background: #202020;
		 color: #bbb;
	}
	
#manage ul ul li img {
		 filter: invert(1);
	}
	
#post code, #collection code, #subpage code {
         background: #202020;
		 color: #33ff33;
    }

body#collection pre, body#post pre, body#subpage pre {
         background: #393e46;
    }
    
body#collection code, body#post code, body#subpage code {
         background: #393e46;
    }

I am a total illiterate when it comes to CCS and the above tweaks came out of tedious trial and error. If you see anything that can be improved feel free to contact me.

Uploading media

Since WriteFreely does not support media uploads we will be using a separate domain for that (e.g. media.example.org) and we will be uploading files on that site using traditional upload methods like ftp, scp or sftp. Then we can reference those images using MarkDown syntax.

  • Prepare the Media store:
$ sudo mkdir /srv/media

Upload a file to it, let's say /srv/media/sample.jpg.

  • Prepare the Nginx virtualhost (/etc/nginx/sites-anailable/media.example.org):
server {
        listen 80;
        listen [::]:80;

        root /srv/media;

        server_name media.example.org;

        location / {
                try_files $uri $uri/ =404;
        }
}
  • Enable the virtualhost:
$ cd /etc/nginx/sites-enabled/
$ sudo  ln -s ../sites-available/media.example.org
$ sudo nginx -t && sudo systemctl reload nginx
  • Enable TLS on the virtualhost:
$ sudo certbot run --nginx --agree-tos -m me@example.org --redirect -n -d media.example.org

Now you can referemce your image using:

![Image name](https://media.example.org/sample.jpg "Image Alt text")

References

  1. https://writefreely.org/start
  2. https://github.com/writeas/writefreely/issues/246#issuecomment-577553211

License

This work is licensed under a Creative Commons Attribution 4.0 International License

Creative Commons Attribution 4.0 International License

#writefreely #nginx #mariadb #linux #debian #freesoftware #blog #cms #activitypub #fediverse #distributed