How to update Ghost on a v15.x TurnKey Ghost appliance

In this post, I'll cover the process required the first time you update the blog software, on a TurnKey Linux Ghost appliance. This applies equally, not matter what platform it's running on; from self hosted VM, to the . Once you've run through this tutorial, in future, you'll be able to simply run:

su - ghost_user -c "ghost update"

If you'd like to get straight in, then please skip straight to where the action starts. If you'd rather take your time and read all the preamble and background, please read on.

This post was inspired by a recent support request in the forums. Fritz has been running the TurnKey Ghost appliance街拍vip第一站 for a while now. Beyond hitting the recent and a few issues he's had while climbing the learning curve, he said that he's been quite happy with it.

街拍vip第一站However, he noted that he'd like update the Ghost software itself. But when he ran the commands recommended in the Ghost docs, he was getting an error. I gave him the command that I was fairly sure should "just work", but he reported that it didn't... :(

So with the expectation that it was something minor, I decided to put aside a few minutes to look into this further. Unfortunately, the minutes turned into hours. So I figured I may as well turn my lemon into lemonade and write it all up as a blog post style step-by-step tutorial.

街拍vip第一站For the next release, we'll try to improve the set up to bring it more into line with the upstream instructions and thus hopefully making it easier for TurnKey users in the future.

has diverged somewhat from the one that the Ghost developers now recommend and support. So these instructions should assist you to get to a state where you can use the ghost cli tool to update and maintain your ghost instance. But first some context of why we do things the way we've done them.

By default, TurnKey installs Ghost as the 'node' user. We use a generic 'node' user to allow us to have some common code across all NodeJS applications. We also have permissions quite locked down (most of the ghost files are owned by the 'root' user by default). This does have a security advantage in some respects. E.g. if a hacker were to hack into your Ghost system front end, the amount of damage that they can do is quite limited (because most files are owned by root). OTOH, as many of the commands need to be run as root, it also means that any malicious NodeJS code that may have crept in (e.g. in NPM libraries) will also be running as root.

A system such as Ghost has the competing desires to be both user friendly and secure. As security is a continuum, rather than an on/off switch, compromises are always required. Usually the compromise of locking things down, is at the cost of ease of use and/or user friendliness, particularly for newer, less experienced users. Getting that balance right is a constant battle, but an important one to keep tweaking the dials on IMO.

街拍vip第一站So there are pros and cons for both set ups. Personally, I still prefer to have root owning stuff. Then, as a system administrator, I can have control over what runs as root. I have to initiate any commands myself, the node user can only do a limited set of tasks without my explicit intervention.

However, in the case of Ghost, that's something of a moot point, as Ghost is designed to be run as a sudo user. It will no longer let you run as root. It's also worth being aware that Ghost is primarily designed for installation on Ubuntu. By default, Ubuntu has sudo installed and root disabled. Whilst I agree that on a desktop system, using sudo (and having root disabled) has serious security advantages. On a server though, not so much... Anyway, that debate continues to rage, so I won't get bogged down in that right now.

So here's how to change the default TurnKey Linux Ghost install and config to bring your Ghost install more into line with how the Ghost developers have designed things to be. And update to the latest Ghost version (currently 3.0.3 at the time of writing).


街拍vip第一站Then set a password for your new user. Note that this will be a "normal" user with full sudo privileges, who can log in via SSH etc, so make it a good password!:

Set a good ghost_user password

passwd ghost_user

As an aside, if you wish to block SSH login for the new 'ghost_user' (advised, unless you plan to log straight in as this new user for future Ghost maintenance tasks), then you can do that like this:

Disable SSH login (optional)

echo "DenyUsers ghost_user" >> /etc/ssh/sshd_config
systemctl restart ssh

街拍vip第一站So we can have a nice terminal (among other things) when we log in as this new user, let's also copy across the default user config files:

Copy across default user config files

find /etc/skel/ -maxdepth 1 ! -wholename /etc/skel/ -exec cp -Rt /opt/ghost/ {} +

For explanation of what this command does, please see . Note that the exclamation mark ('!') means 'not', so essentially the opposite of the match.

Finally, ensure that the new user owns all the ghost files, with the exception of /opt/ghost/content (that needs to be owned by the 'ghost' system user). We also need to ensure that the permissions are all set correctly:

Set up permissions

chown -R ghost_user:node /opt/ghost
chown -R ghost:ghost /opt/ghost/content
find /opt/ghost -type d -exec chmod 00775 {} \;
find /opt/ghost ! -path "/opt/ghost/versions/*" -type f -exec chmod 664 {} \;
街拍vip第一站 The breaking changes between major versions are noted under "Major Version Changelog".

If you want to double check the latest version available:

Check for latest version (optional)

ghost check-update

Or, you could just install the latest update like this:

Install the update

ghost update

街拍vip第一站For other 'ghost' commands, please see the .

街拍vip第一站After running that, everything reported as ok for me. And I double checked by rerunning 'ghost doctor' & 'ghost version' to ensure all was well and I had the latest version.

All looks good, so I exited back to the root account by simply running:


Note that if you logged in via SSH as the 'ghost_user' then running 'exit' will exit the SSH session.

街拍vip第一站Alternatively, you can use this sed line (substitute YOUR_DOMAIN for your actual domain and swap http for https if you don't want https):

sed -i '/"url":/ s|:.*|:"http://YOUR_DOMAIN/"|' /opt/ghost/config.production.json

That's all folks

Hopefully this tutorial on updating Ghost in the v15.x TurnKey Ghost appliance was useful. If you encounter any issues or anything isn't clear, please post below in the comments. Or alternatively open a new thread in the forums.


Fritz Ferrante's picture

Hey Jeremy,

街拍vip第一站 Of course I'm biased, but I love this post and how you go to lengths to explain everything.? However I ran into a problem which is probably fat fingeritis.? When I go to su I get this:

root@ su - ghost_user
Cannot execute /bin/bash/: Not a directory

It would appear that the root doesn't have the permission to use SU?? Here's the two permission sets:


Do I need to add bin/bash to root?

Jeremy Davis's picture

I hope you don't mind but I reformatted your post a little to make it display a little nicer. And it hopefully makes your mistake a little more obvious.

Before I go on, I'd like to thank you for your kind words on my post too. I'm sure my rambling style of writing, combined with the minutia I often get sidetracked with annoys some people who just want to get on with it. So I'm glad it appeals to you. I really try to not just explain what to do and how to do it, but also why. I guess ultimately, I try to be the Linux guy that I wish I'd had access too when I first encountered Linux. And play forward all the occasions where someone took the time to give me the detailed assistance and explanations that were given to me in my early days with Linux.

Anyway, it looks like you've inadvertently added in an extra slash to your ghost_user's shell. If you look closely at the error message and the output of the /etc/passwd file (I assume that's what your second codeblock is?) you'll notice that in the error it's complaining about '/bin/bash/' (note the trailing slash). Then if you compare root's shell and ghost_user's shell (in the second case), you can see that root has '/bin/bash' and ghost_user has '/bin/bash/' (again, note the extra trailing slash). In an attempt to demonstrate my point really explicitly:

root@tkldev ~# ls /bin/bash
root@tkldev ~# ls /bin/bash/
ls: cannot access '/bin/bash/': Not a directory

/bin/bash/ doesn't exist, but /bin/bash does! :)

To fix your ghost_user's shell, use the 'chsh' command (change shell):

chsh -s /bin/bash ghost_user

街拍vip第一站I'm probably overdoing it now, but note no trailing slash on the end of /bin/bash... :)

To double check that ghost_user now has the right shell:

grep ghost_user /etc/passwd

It should now show this:


街拍vip第一站And now the 'su - ghost_user' command should work! :)

Fritz Ferrante's picture

街拍vip第一站 I'm glad my modest search on the interwebz came up with a similar command.? I didn't know the change shell so that's awesome.? I'm now running 3.0.3!!!

Thank for your attention to detail and the desire to educate at the same time.? I now owe you two beers, or wines, or whatever you drink down there.



Jeremy Davis's picture

Awesome, glad that was all it was and that you now have the latest Ghost installed... And extra awesome to hear that your research was on the right track! :)

街拍vip第一站FWIW beer tends to be the drink of choice for most here in Australia, especially men (although lots of women drink beer too). My tastes aren't too refined and I tend to believe that beer comes in 2 distinct flavours; "good" and "very good"! :) Many Aussies are also happy to drink wine too though. Personally, I prefer beer as a general rule, and aren't a big fan of whites, but a nice red with a rare/medium rare steak is hard to beat! :) As a total aside, if you're into red wine and would like to try some Australian wine, then I can recommend Cab Sav from the Margret River or Coonawarra regions or Shiraz from the (almost legendary) Barossa Valley region. The Hunter valley region also produces some good Shiraz IMO.

Jeremy Davis's picture

I've just noticed (and have now fixed) a fairly minor typo, which may have some fairly serious consequence... (Oops!)

It was in the sed line that I gave right near the end. If you ran that, you'll need to fix your /opt/ghost/config.production.json file. I no longer have a ghost server running, so can't explicitly double check, but from the mistake I made, the rest of the command I've posted and memory, you need to double check the "url" line. To do that, run this:

grep '"url"' /opt/ghost/config.production.json

It should look something like this:

     "url": "http://YOUR_DOMAIN/"

If you applied my previous sed command, yours will have a colon missing between "url" and your actual URL.

Fritz Ferrante's picture

街拍vip第一站 Hey Jeremy,

街拍vip第一站 A new update came out!? It looks like I need to get a little help running ghost update.? I'm in the correct directory and use the SU command, but the error is that my ghost user account doesn't have all permissions to files in the directory.? Any thoughts?



Jeremy Davis's picture

Remember that the 'ghost' user account is a Ghost specific account which is intended for internal use only. You need to use the 'ghost_user' account...

街拍vip第一站If you are definately using the right account, then perhaps some of the permissions have got messed up somewhere, somehow? I suggest re-running the "Setup permissions" step again. I.e.:

chown -R ghost_user:node /opt/ghost
chown -R ghost:ghost /opt/ghost/content
find /opt/ghost -type d -exec chmod 00775 {} \;
find /opt/ghost ! -path "/opt/ghost/versions/*" -type f -exec chmod 664 {} \;
Fritz Ferrante's picture

Hey Jeremy, I ended up using the bottom line.? It actually came up in the installation/update commentary.? Those smart folks at Ghost know a thing or two!


Add new comment