You’ve acquired a Virtual Private Server for the low, low price of £3/month and spun it up with Debian Jessie. Now you want to host a blog? Sure, let’s begin.


First, you need something to host. Get hold of Hugo. If you’re living in the future (with Debian 9), this could be as simple as:

sudo apt-get install hugo

If you are running Debian 8 (Jessie) like me, then copy the latest amd64 download link from the Hugo releases and use it instead of mine below:

# Download Hugo
# Install Hugo
sudo dpkg -i hugo_0.16-1_amd64.deb

Check it works by running hugo version, then make a new site:

cd ~
hugo new site blog
hugo new posts/

Then grab a theme and do some configuring as described in the Quickstart Guide. Soon you should be able to run hugo with no arguments, and have it compile your blog to static files in mere milliseconds.


Next you need a webserver. Hugo can serve itself as hugo server, but it’s better to use a real server so you can configure it properly. Nginx is the gold standard for serving static files fast, so install it:

sudo apt-get install nginx-full

Setting up Nginx is a case of writing config files in /etc/nginx/sites-available and symlinking them to sites-enabled when you want them to go live. If all you have is one blog to serve, you can just edit the existing default config. Here’s an example:

server {
    return 301$request_uri;
} # Redirect people typing www.

server {
    listen 80;
    listen [::]:80;
    set $hugo "/var/www/blog";
    root $hugo;
    index index.html index.htm index.nginx-debian.html;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ $uri.html =404;

Then symlink Hugo’s public output to /var/www/blog:

sudo mkdir /var/www/blog
sudo chown $(whoami):$(whoami) /var/www/blog
rm -r ~/blog/public
sudo ln -s /var/www/blog ~/blog/public

Now when you run hugo, it updates the directory Nginx is serving from. Don’t forget to open port 80 with sudo ufw allow http or similar. Try connecting to, hopefully you see something interesting!


Now you have basic web serving working, you should immediately set up SSL. In days of yore, this involved a convoluted dance between generating certificates and signatures and forking over cash, but now it’s $current_year, we have tools to automate this for free!

Again, maybe this is the future and you can simply sudo apt-get install certbot, but I’m writing in the present and presently you must do this to install Certbot on Debian 8:

# Append Backports repository to apt-get's source list
echo "deb jessie-backports main" | sudo tee -a /etc/apt/sources.list
sudo apt-get update
# Install certbot from backports
sudo apt-get install certbot -t jessie-backports

Certbot is the tool that does the legwork of procuring you an SSL certificate, for free, from the good people of LetsEncrypt. Simply run sudo certbot certonly, select the webroot method of authentication, give your domain name, point the webroot at /var/www/blog/ and give your contact email. LetsEncrypt server will then ask certbot to prove it controls your domain by placing files in the webroot that will be served by Nginx. This should happen magically, and a shiny new valid SSL certificate should have been placed in /etc/letsencrypt/live/

Update your Nginx configuration to use SSL, like so:

server {
    return 301$request_uri;
} # Redirect people typing www.

server {
    listen 80;
    listen [::]:80;
    return 301 https://$server_name$request_uri;
} # Redirect http to https

server {
    set $hugo "/var/www/blog";
    root $hugo;

    # SSL configuration
    listen 443 ssl spdy;
    listen [::]:443 ssl spdy;
    # SSL Certificates
    ssl_certificate /etc/letsencrypt/live/;
    ssl_certificate_key /etc/letsencrypt/live/;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ $uri.html =404;

You may also notice we can enable HTTP/2 / SPDY for even faster page loading, just by mentioning it in the listen line. SPDY requires SSL, so here’s yet another bonus for using it. I have also added a redirect from normal HTTP to HTTPS, so just typing into a browser will load the secure version right away.

Reload your site in the browser, and you should see a lovely green padlock now!


As I have discussed before, I wanted to allow comments on these blog posts without pulling in a third-party interface like Disqus. Thankfully, this is possible with tools like Isso, running as a service alongside Nginx and recording comments to a SQLite database file.

I won’t detail the entire installation process for Isso, instead I suggest you follow the documentation, installing and activating virtualenv and then doing a pip install isso. Consider one of their suggestions for making it a proper systemd service as well, I installed Gunicorn for that. More about that later, just run (pip install gunicorn) while you’re in the isso virtualenv for now.

Then drop a basic configuration for isso somewhere like /etc/isso.cfg:

; database location, check permissions, automatically created if not exists
dbpath = /var/lib/isso/comments.db
; your website or blog (not the location of Isso!)
host =

listen = http://localhost:8095
reload = off
profile = off

enabled = true
ratelimit = 3
direct-reply = 3
reply-to-self = false
require-email = true

; set to true if you want to approve comments before they are shown
enabled = false

And make sure whichever user is running isso can access the database file you set (You probably ought to create a user just for it!):

sudo touch /var/lib/isso/comments.db
sudo chown issouser:issouser /var/lib/isso/comments.db

Tell Nginx to proxy requests headed for to isso, running on localhost:8095:

server {
# rest of your nginx config here, as above

    # Add this:
    location /isso {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Script-Name /isso;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://localhost:8095;


Almost there… Now add Isso’s Javascript to your page header (probably something like blog/theme/whatever/layouts/partials/head.html):

<script data-isso="" src=""></script>

…and wherever you want the comments box to appear:

<section id="isso-thread"></section>

Then run isso somewhere with isso -c /etc/isso.cfg run. Run hugo again to publish changes. Reload your article page…et voila! You have started the new Buzzfeed!

Now let’s go back and finish making isso a proper systemd service. We installed Gunicorn (Green Unicorn) earlier, this is a WSGI HTTP server that can run isso instead of it self-running in much the same way Nginx ‘runs’ Hugo. Create a run script that your isso user can access:

sudo touch /usr/bin/
sudo chown root:issouser /usr/bin/
sudo chmod ug+x /usr/bin/

and write /usr/bin/

source /opt/isso/bin/activate
export ISSO_SETTINGS=/etc/isso.cfg
/opt/isso/bin/gunicorn --log-file /var/log/isso.log --preload -b localhost:8095

Check it runs correctly by running it as your isso user, then reloading a page with comments in your browser:

sudo su issouser

Then write a systemd service file, in /lib/systemd/system/isso.service:

Description=lightweight Disqus alternative



Again run isso and reload your browser, this time with sudo systemctl start isso.service.

If all is still well, enable the service on boot with sudo systemctl enable isso.service. Now your comments service will load on boot alongside Nginx!

Going Further

I would definitely recommend initialising a git repository in your blog directory to track changes. From here possibilities are endless, perhaps you want to install gitolite and use push triggers to rerun hugo every time you commit a new blog post and push it? The sky’s the limit. And that’s as far as I’ve got!