Launch by Lunch

Databases, DevOps, and Development

My blog's tech stack: Pelican powered, Dokku deployed

Background

This post goes into the technical components of setting up this blog and some of the merits of the tech stack choices.

For the impatient, the final choice of stack is a Pelican powered static site, styled with a modified Octopress theme, deployed via Dokku, running nginx in a Docker container, and it's all deployed to DigitalOcean droplet.

To put it another way: it's a riddle, wrapped in a mystery, inside an enigma.

Requirements

When setting up a new blog one of the most important (and fun) decisions you get to make is the tech stack that will run it. Besides the topic and posts themselves, this is probably the most important decision. Additionally, the more technologically inclined you are, the more time you'll spend on this decision.

In my case, I'd use a number of platforms in the past and had reduced my core requirements list to the following (in order of importance):

  • Cheap
  • Easy
  • New
  • Fun

Cheap

In 2014 there are many options for cheap hosting so cost was not much of a concern.

For static content there are a number of free hosting options such as Tumblr or GitHub pages. You can also use services like Amazon S3 to host a website for next to nothing.

For example our company's website, https://www.jackdb.com/, is a static site hosted on Amazon S3. The bill for it is literally pennies a month and, since it's on S3, we never worry about scaling it up to handle large influxes of traffic.

Even sites with dynamic content can be hosted for free or relatively cheaply. Heroku gives you a free web dyno per application. You use it to either run a dynamic site (ex: Wordpress or Ghost) or a static site (ex: nginx serving a pregenerated site).

Easy

Easy in this context means it should be easy to preview an article while writing and the final deployment step should be automated. The ideal workflow for writing will look something like this:

  1. Write something
  2. View what it'll look like in the browser
  3. Not done? Goto step 1
  4. Publish to the production site

Saavy readers will recognize this as the writing equivalent of a REPL. Since a big part of writing is minimizing distraction (so you can actually focus on writing), it's important to have an efficient way to "test" your posts (i.e. the step 2 to 3 flow).,

Publishing should be as simple as possible, preferably from the command line. This usually means it should be git push style deploy.

Note that being "easy" does not refer to the initial setup of the platform. On the contrary, the newer the platform the more likely there will be complications with the initial setup. If they're not show stoppers then that would probably increase the "fun" factor though.

New

By "new" I'm referring to technology choices that are new to not just me, but the world as a whole. Like any good technologist I'd like to use this as an opportunity to experiement with new technologies.

There's a high correlation between new and fun.

Fun

The "fun" factor of setting up a new website is derived from the new (to you) technology you get to experiment with. The more twists, turns, and hacks to set it up, the more fun you're going to have. A decent gauge of whether something is going to be fun is whether you'd want to listen to someone else describe the set up process.

On the low end of the fun scale (arguably negative fun...) is using a hosted service like Tumblr. There is no real set up work involved (just sign up and optionally pick a theme) and nothing new to experiment with.

The opposite extreme would be writing your own blogging software. It would be a very fun exercise and would make for a good write up on its own, but you probably would not get much writing done until it's finished.

Static Site

I decided early on that I wanted the site to be entirely static. A static site increases your hosting options and will generally scale very cheaply and easily. Properly deployed (i.e. ngninx with the right cache headers), a static site on a single server should handle any reasonable amount of traffic. Deciding on having the site be static also helps with the paradox of choice in picking a tech stack.

The biggest limitation with a static site is that all the content that you serve is generated in advance. This means you can't have any dynamicly generated content and any user interaction with your site will be handled by external services.

For example, if you want to add comments to a static website you could use a service like Disqus. If you'd like to have a sign up form to collect email addresses from your readers, you can use a service like Mailchimp or Wufoo.

Generators

The end result of a static website is a collection of HTML, CSS, and other asset files that you'd deploy to a web server. Rather than generate them by hand (i.e. copy paste each page...) most people use a static site generator. These are pieces of software that automate the process of generating the HTML pages of your site.

Some popular features for static site generators are:

  • Allow writing posts in a light weight syntax such as Markdown rather than straight HTML
  • Automatically appying common CSS styling and theming to all your posts
  • Generating index and listings pages
  • Grouping pages based on post tags or content

See here for a great list of alternatives. There are a lots of them.

Pelican

The static site generator I decided to use is Pelican. Here are some quick highlights of it:

  • Written in Python
  • Under active development
  • Lots of themes available
  • Many deployment options (the final result is a static site)
  • Easy to install

I don't write a large amount of Python code but it is a language I like a lot. The syntax is clean, it has a wealth of libraries, and it runs on just about every platform. I didn't have to modify the source for Pelican in setting up my blog but, if I did, I know it'll be pretty easy.

Although I didn't particularly like Octopress, I do like the standard Octopress theme. Thankfully, there is a Pelican theme for it! I used it as a base and added/updated/fixed a few things. You can find my fork here.

Setup

Although installing Pelican was easy enough, getting a sample site up took a bit of work. All the building blocks are there but the documentation and initial setup could use a bit of work.

Pelican Quickstart

Pelican includes an executable, pelican-quickstart, that automates the creation of a new site. It asks you a couple basic questions (your name, site name, URL, etc) and creates an site directory. It even includes a development script that watches your file system for changes and automatically regenerates the static site (REPL solved!).

Unfortunately, the site it generates is really empty. A lot of the standard customizations that you'd expect to do to a new site are missing:

  • No sample post (where do I write?)
  • No favicon defined (where does it go?)
  • No sample images (where am I suppoed to add them?)
  • No sample extras (where do I put misc files I want to add like a PGP key?)
  • No markdown code highlighting (how do I enable this?)

Barebones Pelican Blog

It's perfectly understandable why the base site that is generated doesn't include any of these. It's meant to be as lean as possible and added atop.

Still though, I prefer a working example I can modify. When working with a new piece of technology it's always easier to tweak a functional existing system and comment out blocks you don't need then add things you do.

To simplify this for the next person who'd like to use Pelican, I created a bare bones example of a Pelican powered blog. You can find it here: https://github.com/sehrope/pelican-dokku-bare.

Some highlights of it are:

  • Includes a customization list (i.e. list of places to edit to make it yours)
  • Includes a sample post
  • Includes a sample image (and how to link to it in a post)
  • Includes a sample PGP key (to show how to include extra files)
  • Enables markdown code highlighting
  • Custom 403, 404, and 500 error page via git
  • Defaults to my tweaked Octopress theme
  • Deploys out of the box to a Paas (i.e. Heroku or Dokku) via git

To use it just clone the repo and start it up:

# Clone it locally:
$ git clone https://github.com/sehrope/pelican-dokku-bare my-blog

# Enter the cloned directory:
$ cd my-blog

# Start up the development server:
$ ./develop_server.sh start

Now if you add or edit a file in the content/posts directory it will automatically show up. To further customize it follow the instructions in the README.

Docker, Dokku, and DigitalOcean

The final (and most fun) part of the setup was the install for Docker and Dokku on DigitalOcean. Docker and Dokku definitely fall into both the "new" and "fun" categories of technology. It was so fun I wrote about setting it all up and how to improve it further.

In reality the Dokku server set up was the first thing I did — the blog itself was started because I wanted to write about the setup.

Once Dokku is configured, this blog is just another application deployed to my DigitalOcean droplet. There is no special setup or configuration for it. I just added it as a git remote and git push to it whenever I'd like to publish new content.

All it takes is:

# Add the remote Dokku production site:
$ git remote add production dokku@launchbylunch.com:launchbylunch.com

Then I can publish the blog via:

$ git push production master

Publishing takes about a minute as each time it rebuilds the entire application from scratch. The Pelican site generation takes barely a second and most of the deploy time is spent building the dependencies (ex: the buildpack compiles nginx from scratch). It's all fully automated and reproducible though so you don't really think about it.

SSL

The only addtional configuration I did for this site after deploying it to my DigitalOcean droplet was to add SSL. Dokku has support for it out of the box so it was really just a matter of creating the server's private key, getting it signed, and uploading it.

First generate a 4096 bit key. You'll be prompted for a passphrase (don't forget it!).

$ openssl genrsa -des3 -out server.key.secure 4096
Generating RSA private key, 4096 bit long modulus
[... truncated ...]
Enter pass phrase for server.key.secure:
Verifying - Enter pass phrase for server.key.secure:

Then create a password-less version of the private key. This will be the file we upload to our server. It doesn't have a password so that our web server can be restarted without manual intervention.

$ openssl rsa -in server.key.secure -out server.key
Enter pass phrase for server.key.secure:
writing RSA key

Finally, create the certificate signing request. Most of the fields are arbitrary. The important one is the "Common Name" which should match the hostname of your server:

$ openssl req -new -key server.key.secure -out server.csr
Enter pass phrase for server.key.secure:
You are about to be asked to enter information that will be incorporated
into your certificate request.
[... truncated ...]
Common Name (e.g. server FQDN or YOUR name) []:launchbylunch.com
[... truncated ...]

Now you should have three files:

  • __server.key.secure __ - the private key
  • server.key - the private key with the password removed
  • server.csr - the certificate signing request for you key

Next, you'll need to have your server.csr signed by a certificate authority. I use Namecheap as my registrar and they offer $1.99 SSL certifcates (for one year) when you buy a domain. I've got a bunch of them accumulated so I used one of those.

In 10-15 minutes you'll receive a confirmation email to prove domain ownership. Once that is complete you'll receive a reply from the certificate authority with the signed certificate. If they include an intermediate certificate (ex: the Namecheap Positive SSL I describe above includes two) then concatenate them into a single server.crt.

Finaly upload server.key and server.crt to your server and place the two files in ~dokku/<app>/ssl and redeploy the app. In my case that's ~dokku/launchbylunch.com/ssl.

Upon startup Dokku will detect the SSL certificate and configure it's nginx router to redirect traffic from port 80 (HTTP) to 443 (HTTPS). It also does a good job of setting up nginx with PFS. The only thing it doesn't include out of the box is HSTS but we'll add that later.

Final Thoughts

The end result of all this is that I have an easy to deploy to blog running for $10/mo. In reality I could host it for free (it's static content after all) but I like the control of running the server myself (and it's way more fun). Plus, as it's just one of the many Dokku deployed apps on my server, the cost itself is shared, and I have an excuse to keep the server running.

Pelican itself is really pleasant to use. Besides a couple hiccups in the initial setup I haven't had any issues with it and would recommend it to anyone else setting up a blog.

Did you try this out or do you have a more cool/fun/interesting setup for your site? Tell me about it.