Installing Ghost via Github on Uberspace

14/09/05 Updated: Removed depricated steps.
14/01/21 Changed the Set up a daemon part (added step 2) to fix the broken inline file uploading feature.[thx]

Ghost is the way to go when it comes to blogging: it utilizes Markdown, has a lean admin interface and is written in Node.JS.

Uberspace is hosting company based in Germany. They offer a well rounded managed hosting solution with world-class support and a extensive documentation (written in german).

Uberspace + Ghost = Uber-Ghost? You bet! But to get the Uber-Github-Auto-Update-Ghost we really want, we need to take a different route:

Our route:
1. Prepare Uberspace
2. Install Ghost
3. Configure Ghost
4. Set up HTTPS
5. Set up a daemon
6. Control Ghost
7. Set up an auto update script

Prepare Uberspace

  1. In this tutorial I assume that you have set up a domain for Uberspace that you can use for your Ghost installation.
  2. Generate a .npmrc:
    cat > ~/.npmrc <<__EOF__ prefix = $HOME umask = 077 __EOF__
  3. Install Grunt:
    npm install --production -g grunt-cli

Install Ghost

  1. Create a directory for the domain (e.g. that you want to use:
    cd /var/www/virtual/$(whoami)/ && mkdir /var/www/virtual/$(whoami)/YOUR_DOMAIN_NAME
    Uberspace will then automatically redirect all requests to that domain there.
    Do not place your Ghost installation in your html folder (or symlink it from there) except you know what your doing. That would make your config files (including passwords) available via the web.
  2. Open the new directory:
  3. Clone the Ghost repository:
    git clone ./
  4. Switch to the stable branch:
    git checkout stable
    Sitenote: The stable-part is explained here.
  5. Install the npm packages and generate files (don't replace the XX...-part):
    export TMPDIR=$(mktemp -d /tmp/XXXXXX) npm install --production npm install --production grunt bower
    Sidenote: We need the export ...-part because of a problem with SQLite as explained here.
  6. Install required additional packages:
    npm install --production matchdep grunt-concat-sourcemap grunt-contrib-clean grunt-contrib-compress grunt-contrib-concat grunt-contrib-copy grunt-contrib-jshint grunt-contrib-uglify grunt-contrib-watch grunt-docker grunt-ember-templates grunt-es6-module-transpiler grunt-express-server grunt-mocha-cli grunt-shell grunt-update-submodules
  7. Run Grunt to generate asset files:
    grunt init grunt prod

Configure Ghost

  1. Pick a number between 61000 and 65535 for the port we will use internally:
    echo $((RANDOM%4536+61000))
  2. Verify that this port is not already in use by some other Ubernaut (=customer of Uberspace):
    /usr/sbin/ss -ln | fgrep YOUR_PORT_NUMBER
    No output means it's free.
  3. Now make a config file:
    nano config.js and enter the following.
    Sidenote: If you have not set up a email address for your domain you can use a different SMTP server or just leave out the mail: section.
var path = require('path'),

config = {
  production: {
    url: '',
    mail: {
      fromaddress: '',
      transport: 'SMTP',
      options: {
        host: 'MAILSERVER',
        port: 587,
        auth: {
          user: '',
          pass: 'PASSWORD'
    database: {
      client: 'sqlite3',
      connection: {
        filename: path.join(__dirname, '/content/data/ghost.db')
      debug: false
    server: {
      host: '',
      port: 'YOUR_PORT_NUMBER'
module.exports = config;

Sitenote: Leave http:// in there even if you use https. It caused problems for me otherwise.

Set up HTTPS

  1. If you have your own SSL certificate:
    If you have your own SSL certificate and have already set it up for your Uberspace then you can redirect all HTTP traffic to HTTPS. In that case follow the next step and replace ^ghost(.*) with ^(.*).
  2. If you have no own SSL certificate:
    Uberspace provides you with a free HTTPS certificate that you can use right away. The downside is that this default cerificate will raise a warning in webbrowsers since it's not specifically meant for your domain.
    Since we do not want to generate warnings but still want to have some security when logging in into Ghost we only force HTTPS for the administrative part of it.

    Set the rewrite rules:
    nano .htaccess and enter:

RewriteEngine On
RewriteBase /

RewriteCond %{ENV:HTTPS} !=on
RewriteRule ^ghost(.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

RewriteRule ^(.*) http://localhost:YOUR_PORT_NUMBER/$1 [P]

Set up a daemon

You will be able to control your Node.JS server via this deamon.
Sidenote: You do not necessarily have to prepend node- like I do here but I recommend it since it's more telling. Same goes for the domain name part: you can also use an arbitrary name if you like. Just be sure to change it everywhere if you do.

  1. Set up a service for Ghost:
    test -d ~/service || uberspace-setup-svscan uberspace-setup-service node-YOUR_DOMAIN_NAME \ env NODE_ENV=production \ node index.js 2>&1
  2. Fix the generated run file:
    nano ~/service/node-YOUR_DOMAIN_NAME/run
    and add the following anywhere above the last line:
    cd /var/www/virtual/$(whoami)/YOUR_DOMAIN_NAME
  3. Restart the daemon:
    svc -du ~/service/node-YOUR_DOMAIN_NAME

Now your blog should be up and running!

Control Ghost

This is how you can control your Node.js server:

  • To bring it up:
    svc -u ~/service/node-YOUR_DOMAIN_NAME
  • To bring it down:
    svc -d ~/service/node-YOUR_DOMAIN_NAME
  • To restart it:
    svc -du ~/service/node-YOUR_DOMAIN_NAME

Set up an auto update script

Caution: Be aware that updating your Ghost installation this way could potentially wreck your Ghost database some day. So just keep in mind that Uberspace keeps daily backups of all of your files (including your database file) for 14 days.

Sidenote: Like before the run-update- and update- parts are just for the sake of a better overview and could be changed.

  1. Set up the superior[why?] cron alternative runwhen:
    runwhen-conf ~/etc/run-update-YOUR_DOMAIN_NAME ~/bin/update-YOUR_DOMAIN_NAME
  2. Open the config file:
    nano ~/etc/run-update-YOUR_DOMAIN_NAME/run
  3. Modify it so that you have e.g.:
  4. Now generate the update script:
    nano ~/bin/update-YOUR_DOMAIN_NAME and enter:

    source ~/.bash_profile  
    export TMPDIR=`mktemp -d /tmp/XXXXXX`  
    svc -d ~/service/node-YOUR_DOMAIN_NAME/  
    git pull  
    git submodule update  
    npm install --production  
    grunt prod  
    svc -u ~/service/node-YOUR_DOMAIN_NAME/  
  5. Start runwhen:
    ln -s ~/etc/run-update-YOUR_DOMAIN_NAME ~/service/update-YOUR_DOMAIN_NAME
    Hint: Your update script will also generate a log file which you can read via:
    tail -f ~/service/update-YOUR_DOMAIN_NAME/log/main/current | tai64nlocal
    If you want to manually run your update script you can enter:
    svc -a ~/service/update-YOUR_DOMAIN_NAME/

Ressources that helped me out:
- Some problems self-hosting ghost, with solutions
- Solving “found or unreadable: bourbon/_bourbon” with Ghost
- updating with the latest changes

The author

Written by Per

Free software enthusiast and transhumanist residing in Stuttgart, Germany.

comments powered by Disqus