Setting up my personal website using Nikola

Last year, I had to migrate my web pages very quickly to my own server because changes in my Institute’s website made it difficult for me to continue to host my web pages there. My old web site was set up more than two decades ago using raw html, and it used php for on-the-fly generation of content. Migration provided an excuse for me to move to static web pages for security and performance reasons. Since page generation happens locally, I can use any software that I find convenient without worrying about “hardening” it for security. Also the web server that I have in the cloud is relatively low on memory and processor resources, and therefore I would like to do as much of the processing locally as possible.

I tried both Jekyll and Nikola, and chose Nikola for two reasons. First, I found Nikola’s template engine, jinja2 to be more powerful. In particular, the ability to use any Python method in the template provides a lot of power. Second, and less important, I am more comfortable programming in Python than in Ruby.

Initially, I was put off by Nikola’s documentation which seems to be directed at the novice user, and seems to discourage the kind of user who wants full control over the whole process. The documentation nudges the user to choose some theme and then tweak it using a configuration file. The problem with this approach is that the “theme” controls not only the appearance but also the content because it contains all the templates. To exercise full control, it is necessary to write these templates myself; I would of course borrow liberally from templates written by more experienced and competent people, but I would not be satisfied without knowing what each template does.

Twice, I went back to Jekyll because its documentation is more friendly for a do-it-yourself user. But then, I persevered and was able to find what I wanted by reading the sections in the documentation on Nikola internals , Nikola template variables, Extending Nikola (plugins), then reading the template files inside the base-jinja theme, and finally as a last resort, the Nikola source code and the Nikola plugins source code.

Minimal working example (MWE)

I now describe what I did to get a simple web site with just one page. Once, the minimal working example (MWE) is understood, it is easy to build on it and develop a full fledged website.

  • Create a new site
nikola init -q new-website-nikola
cd new-website-nikola/
mkdir files/css
mkdir files/pages
  • Create the single page in the website, pages/, in markdown format with metadata in yaml format.
  • Create the css file for the site, files/css/main.css
  • Create a theme called mytheme manually without parent theme. The minimal theme consists of only three files.
    1. mytheme.theme tells Nikola that the theme uses the jinja template engine.
    2. base.tmpl is the base for all other templates. Its function is to (a) create an html header with a link to the style file main.css, (b) set the title from the page/post title, (c) use the same title for the first level heading, and (d) insert the post contents (from a child template) in the html body.
    3. page.tmpl is the only child template in this minimal web site. It simply populates the contents of a web page for use in the parent base.tmpl.
  • The steps for creating this minimal template are:
    • Create the requisite folders with
mkdir -p themes/mytheme/templates
  • Create themes/mytheme/mytheme.theme with following contents
engine = jinja
  • Create themes/mytheme/templates/base.tmpl with the following contents.
{#  -*- coding: utf-8 -*- #}
<!doctype html>
<html lang="en">
    <meta charset="utf-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="/css/main.css">
    <h1>{{ title }}</h1>
       {% block content %}{% endblock %}
  • Observe that this template obtains the title of the page using the per-page local variable title
  • Create themes/mytheme/templates/page.tmpl with the following contents
{#  -*- coding: utf-8 -*- #}
{% extends 'base.tmpl' %}
{% block content %}
    {{ post.text() }}
{% endblock %}
  • Observe that this template obtains the text of the post by calling the text method: post.text()
  • Create a minimal which disables most plugins and builds only a bare bone web site: no blog, no gallery, no sitemap. The contents of are:
THEME = "mytheme"
TIMEZONE = "Asia/Kolkata"

POSTS = ()
    ("pages/*.rst", "", "page.tmpl"),
    ("pages/*.md", "", "page.tmpl"),
    ("pages/*.txt", "", "page.tmpl"),
    ("pages/*.html", "", "page.tmpl"),
INDEX_PATH = "blog"

    "markdown": ['.md', '.mdown', '.markdown'],
    "html": ['.html', '.htm'],


# Plugins you don't want to use. Be careful 🙂
DISABLED_PLUGINS = ["redirect", "render_galleries", "render_listings",
                    "render_sources", "render_taxonomies", "robots",
                    "scale_images", "create_bundles", "sitemap"]
# Based on output of "nikola list", the ENABLED_PLUGINS are
#            "copy_assets", "copy_files",
#            "post_render",
#            "render_pages", "render_posts", "render_site"
  • We are now ready to build and test this site.
nikola build
nikola serve

Extending the minimal working example

Extending the MWE consists largely of (a) adding more content in the form of markdown files for each page in the website, and (b) adding more bells and whistles to the template files. It may also be necessary to add more templates for pages that we want to render differently from the base template. In the yaml metadata of any page, we can add a line telling Nikola to use a different template. For example:

template: special.tmpl

If we just want to add some extra content while continuing to use the base template, we add something like the following in the body of the page:

{{% template %}}
  {% include 'extra.tmpl' %}
{{% /template %}}

Unlike Jekyll‘s Liquid templates that work in normal markdown files, Nikola’s jinja2 works only in template files. The {{% template %}} tag allows jinja2 code to be inserted inside a markdown file. Another useful technique uses shortcodes. If we create a template file myfunction.tmpl in the shortcodes folder, we can call it like a function from a markdown file in the form {{% myfunction %}}. Arguments can be passed to the myfunction in two ways:

  1. In the post, we give the argument between {{% myfunction %}} and {{% /myfunction %}} tags. In the myfunction.tmpl shortcode, the argument is accessed as {{data}}.
  2. In the post, we pass named arguments and we access them in the shortcode using the same names. For example, the post contains {{% myfunction message="Hello World!" %}}. In the template, we can access the argument by its name {{ message }}.

The complication that I encountered while using shortcodes was in accessing the post metadata. While post.meta('xyz') worked in normal templates, it did not work in shortcodes where I had to use post.meta['en']['xyz'] (I discovered this syntax by reading the Nikola source code).

Generating content from data files

In my website, my research papers are listed in different ways on different pages. In one page, only recent papers may be listed, while in another, all papers are listed. They may also be classified, sorted or formatted differently. The sensible way to manage this is to create a data file containing all the information about every paper and then write some code in say Python to process it in different ways.

The nice things is that jinja2 has for loops and if statements to loop through all records in a data file and then pick up only what is needed. Moreover, it is possible to call any Python method on any of the objects. This means that the Python code that I was thinking about can actually be replicated in jinja2

In my case, the data is in yaml files with each record having fields for title, year of publication, and other relevant information. These files have to be in the data folder, and are referred to in the template using the file name. For example, data/books.yaml contains data about all the books that I have written, and one of the fields is title for the book title. In the template, I can list the titles of all the books using a for loop

{% for book in data.books %}
{% endfor %}


In my case, some content is more complex and not easy to generate using a template. It is much better to use a programming language to do this job. I therefore wrote a Nikola plugin which is basically python code that will be called by Nikola. The ConfigPlugin category of plugin is used because Nikola scans for posts after this category of plugins has run. The my_generator class extends the ConfigPlugin class. Before rendering the site, Nikola will call my_generator.set_site. This method generates and writes the extra files and then calls super(my_generator, self).set_site(site). This tells Nikola to scan the whole site again. Pages generated in this plugin will therefore be processed.


The last step was to migrate my blog to Nikola. This was a little weird because Nikola is designed for blogs, and normal web pages are a kind of afterthought. What I did on the other hand was to migrate all the web pages except the blog. Migrating the blog was a more complex and time consuming process that I will cover in a separate blog post.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s