published: 2018-06-26
title: by blog - server set-up
tags: free-software, wiki, miscellaneous
previous [‘dgplug Newsletter #4’] next [‘How to browse blocked sites with Adblocker’] parent directory [‘Blog’]
This whole blog is mainly about Free Software and some
code snippets
and patterns
I use, that I spent
some time with. That’s why I dedicate this index page to the creation on
this blog itself. For now the server and project is quite fresh, I will
update it with any changes when they approach.
I started this blog on wordpress[4], which is a nice service to quickly get started blogging. However, after some time I started to be annoyed by some aspects;
I don’t want readers to be confronted with ads if possible, but I
also didn’t want to apply for a paid subscription. Not because I’m too
greedy to pay for a good service, but because despite the offer of
plugins I don’t feel free to customize my page the way I want to. Also I
cannot tell which services and JavaScript
packages run on
the page that I identify with, that may track readers of my blog
etc.
So I decided to go with my own server (or better rented VM) that I have the most possible freedom and security on.
I took inspiration from Anwesha Das in her posts on how to set up a server[5] and how to use Let’s Encrypt[6] with nginx to provide readers with a secure connection. I rent a small VM for ~ 10 Euros per annum, nothing fancy since I hardly have any hardware requirements, installed CentOS 7.5 and did some hardening as described in Anwesha’s post.
anweshadas_server [‘how to set up a server’] anweshadas_letsencrypt [“how to use Let’s Encrypt”]
To be prepared for my own failure, I’ve set up most things using Ansible playbooks, so I can easily recover a fresh server state if I mess up anything. To install basic packages I do stuff like
- name: "Installing epel repository and yum packages"
yum:
pkg: "{{ item }}"
state: installed
with_items:
- epel-release
- yum-utils
- name: "Update packages"
yum: name=* state=latest update_cache=yes
- name: "Installing some basic packages"
yum:
pkg: "{{ item }}"
state: installed
with_items:
- git
- man
- python-pip
- cronie
- vim
As webserver and proxy I use nginx. Certbot[7] is a tool to request signed certificates from the Electronic Frontier Foundation (EFF). Encrypting the own website has never been that easy!
- name: "Installing nginx"
yum:
pkg: "{{ item }}"
state: installed
with_items:
- nginx
- yum-utils
- epel-release
- name: "Update packages"
yum: name=* state=latest update_cache=yes
- name: "Install Certbot"
yum:
pkg: python2-certbot-nginx
state: installed
Again I could re-use many of the nginx settings shared in Anwesha’s post. I don’t need the Docker parts, however. With the signed ssl certificates in place, I can now safely redirect all http web requests:
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name blog.schubisu.de;
return 301 https://blog.schubisu.de$request_uri;
}
Once certbot
was running correctly, I’ve set up a
cron job
to renew the certificate regularly:
0 0,12 * * * certbot renew
I’m quite happy with this server configuration, it feels small, simple and secure.
Lektor[8] is a Python based Content Management System (CMS) to create and maintain static websites. It’s a versatile and hackable tool, equally suitable for quick and small blogs like this or big professional projects.
As the documentation[9] is excellent, I won’t repeat it here. Lektor
uses ini-files
do declare models, yaml-like
files called contents.lr
as model instances and the
powerful ninja2 template engine to render the HTML.
In the yaml
syntax of the contents.lr
files, content can be specified as plain text, html or markdown (there’s
a plugin that also supports restructured text). Model instances can be
queried in the ninja2 templates and in model definitions; the syntax
feels quite familiar to me as a django user.
While everything in Lektor is configured in plain text files, the CMS allows editing and managing content via admin web interface as well.
This blog is based on one of the many useful guides[10] from the Lektor documentation. It declares two models, one that describes blog-post instances, and one that describes the page that holds blog-post instances as children.
What I’m particularly happy about is the fact, that I don’t need
anything of this on my server. I configured the Lektor project to deploy
the page with rsync
. A simple
lektor build && lektor deploy
will render the
static HTML pages and push them to the server. I’m not sure if it could
be any easier.
There are some minor features I wanted my blog to equip with.
I’m not a big fan of JavaScript
and was able to avoid it
throughout the page so far. However, to make the
code snippets
I include easier to read I at least want to
have code highlighting in my
<pre><code>...</code></pre>
tags. I
found Prism[11] very easy to set up and implement.
Apart from the theme I can choose which languages to support to avoid
the js
file to become unnecessary big. Finally I add
class="language-<programminglanguage>"
to enable
proper highlighting.
As stated above, I was not very happy with a JS solution on my blog.
Learning more and more about the Lektor ecosystem (just a few seconds of
searching would have brought me there quickly) I discovered the lektor
plugin markdown-highlighter
which makes use of Pythons
pygments
module. I don’t know why I opted for a JS solution
in the first place, how could I forget that I have the full power of
Python in hands with Lektor? Now the most difficult question is which
style to choose among the wide range that pygments
offers.
The Lektor documentation already gave examples on how to sort instances by date. Since I may include posts of very different topics I also wanted to add tags, and the possibility to filter posts by tags.
Therefore I add another set of models; tags.ini
and
tag.ini
. While tags.ini
is quite boring (it
specifies that it’s children are of type tag
), the trick is
done by the children
directive in
tags.ini
:
[children]
replaced_with = site.query('/blog').filter(F.tags.contains(this))
at the same time, I added a tags
field to the
blog-post
model:
[fields.tags]
label = Tags
type = checkboxes
source = site.query('/tags')
To easily update readers about new or changed contents on my site, I
want to have an rss.xml
file that contains the necessary
information about the blog posts in the rss
standard
format.
To always keep the rss.xml
file up to date, I wrote a
plugin
for Lektor that uses setup_env
as entry
point, so everytime Lektor builds the HTML sites, the xml file is
created.
I use the Python package feedgen[12] to generate and export the final
rss.xml
.
feedgen_repository [‘feedgen’]
from lektor.pluginsystem import Plugin
from feedgen.feed import FeedGenerator
from datetime import datetime
import pytz
import os
class RssPlugin(Plugin):
= u'lektor-rss'
name = u'Plugin to create an atom rss feed from selected models.'
description = "Schubisu's blog feed"
title = {
author 'name': 'Robin Schubert',
'email': 'robin.schubert@gmx.de'
}= '/home/robin/gitrepos/myserver/myblog/content'
destination_dir def on_setup_env(self):
= self.env.new_pad()
pad = FeedGenerator()
fg id('https://blog.schubisu.de')
fg.self.title)
fg.title(self.author)
fg.author(='https://blog.schubisu.de/rss.xml', rel='self')
fg.link(href"Schubisu's blog feed")
fg.description('en')
fg.language(for post in pad.query('blog').all():
= fg.add_entry()
fe 'title'])
fe.title(post[id(post.path)
fe.="http://blog.schubisu.de" + post.url_path)
fe.link(href'body'].source[:200] + "...")
fe.description(post[
fe.published(
datetime.combine('pub_date'],
post[min.time()
datetime.=pytz.timezone('Europe/Berlin'))
).replace(tzinfo
)="http://blog.schubisu.de" + post.url_path)
fe.content(srcself.destination_dir, 'rss.xml')) fg.rss_file(os.path.join(
In the <head>
tag of my HTML base layout I add
<link rel="alternate" type="application/rss+xml" title="Schubisu's blog feed" href="https://blog.schubisu.de/rss.xml">
to notify the browser about the rss feed.
While this was a nice exercise, I realized that the HTML content
should better be escaped in the xml file. While browsing for the
simplest solution I had to find out that there is a plugin
for Lektor, that generates my feed the way I want it. How could I think
that this has not been done before? I was searching for rss
plugins before but it turned out that the package is actually called
lektor-atom[13].
So a simple lektor plugins add lektor-atom
and
configuring the configs/atom.ini
almost did it for
me. The lektor plugins
statement added
lektor-atom = 0.3
to my .lektorproject
file,
however, I’m using a Python2.7 based version that doesn’t have version
0.3
in the PyPI. So switching to 0.2
installed
the requirements smoothly and I’m happy with the result.
Although the Lektor documentation does not recommend to install the
package via pip, this was the easiest solution for me to get Python3
support. Another option would have been to modify the package build in
the AUR installation. I don’t experience any issues with the pip
installation, however. So upgraded the to lektor-atom 0.3
again Yay!
I have licensed the contents of this blog under a Creative Commons[14] Attribution-ShareAlike license. Anything on this site can be copied, changed and re-distributed provided a similar free license is used.