How to install Ghost on Ubuntu 18.04 LTS?

Hello Ghost Community!

I’m a new ghost in town, and I recently tried to install Ghost for the very first time. It was about as troublesome as I expected. Despite following the official installation guide for Ubuntu.

A full guide for installing, configuring and running Ghost on your Ubuntu 16.04 or 18.04 server, for use in production

I have installed it on a VPS running Ubuntu 18.04 that I made specifically for this purpose, so it was a clean slate before I started the installation. I have done this two times now with the same result. That is, Ghost appears to be running if I issue the ghost status command, but it is not accessible from the outside world.

This is what my Ghost looks like…

I also installed it one time on a Windows 10 VM just to see what to expect and it worked immediately. Of course, it was not accessible from the outside world. But it was very satisfying to see it running! :relieved:

So my guess is that it’s not Ghost, it’s NGINX! It’s just that… I used the official documentation to install it, following all instructions to the best of my ability, and using all the prescribed ingredients such as Ubuntu 18.04 LTS.

So where did I go wrong I wonder?.. and should I give up and just get Ghost as a managed service, or perhaps host my own with a company that has a one click installer for it?.. I’m almost there, so I would like to try to make this work one more time before I go elsewhere or do something else.

My Ghost-CLI version: 1.13.1

This the official guide for self-hosting Ghost using our recommended stack of Ubuntu 16.04 or 18.04.

I am running Ubuntu 18.04 LTS.

NGINX (minimum of 1.9.5 for SSL)

My nginx version: nginx/1.14.0 (Ubuntu). I have tried with and without SSL setup.

A supported version of Node.js

According to that list, “10.x (Node v10 Dubnium LTS)” is recommended. My node version is 10.19.0.

MySQL 5.5, 5.6, or 5.7 ( not >= 8.0)

My version is 5.7.29.


Since I am running Ubuntu, yes, I am using Systemd. I can also see it when I issue the ghost status command.

I will start ghost setup again. But before I do, I have some questions about these instructions (sanity check).

Blog URL

Enter the exact URL your publication will be available at and include the protocol for HTTP or HTTPS. For example, . If you use HTTPS, Ghost-CLI will offer to set up SSL for you. Using IP addresses will cause errors.

In other words, I cannot use an IP address like ? So I need to have a registered domain name like and enter that in here?

MySQL hostname

This determines where your MySQL database can be accessed from. When MySQL is installed on the same server, use localhost (press Enter to use the default value). If MySQL is installed on another server, enter the name manually.

So as long as MySQL is installed on the same server as Ghost I can just leave it at default?

MySQL username / password

If you already have an existing MySQL database enter the the username. Otherwise, enter root . Then supply the password for your user.

Do I have an existing MySQL database if I just installed Ghost and MySQL on a clean slate? Password for what user? The root user? See I’m confused about “root” and “root”. How is this related to the ALTER USER command given earlier in the guide? I mean this…

If you’re running Ubuntu 18.04, a password is required to ensure MySQL is compatible with Ghost-CLI . This requires a few extra steps!

# Replace 'password' with your password, but keep the quote marks!
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

This is where it gets confusing. Is this “root” the same “root” user I’m being asked about and is it this password that I need to provide? Or is it the system root user and password on the server that I need to provide?

Ghost database name

Enter the name of your database. It will be automatically set up for you, unless you’re using a non -root MySQL user/pass. In that case the database must already exist and have the correct permissions.

So, if I do not provide a root user and password in the previous step, then a database will not be created for me automatically? Then I don’t see any reason not to use root. It would require some extra work on my part, and since this is all news to me I wouldn’t want that. Besides, it defies the purpose of an automated setup script like this. The ideal would be for this script to ask me or even check automatically if I have an existing database, and ask if I want to use that one with Ghost and if so provide the user and password, otherwise offer to create a new database for Ghost.

Set up a ghost MySQL user? (Recommended)

If you provided your root MySQL user, Ghost-CLI can create a custom MySQL user that can only access/edit your new Ghost database and nothing else.

So, if I understand correctly, this step will create what I previously did not have (assuming I did not have a “non-root MySQL user/pass”)? And pair it up with my new Ghost database that was created in the previous step?

Set up NGINX? (Recommended)

Sets NGINX up automatically enabling your site to be viewed by the outside world. Setting up NGINX manually is possible, but why would you choose a hard life?

Ah! My Achilles’ heel! :relieved: It’s a hard life for sure. I’m note sure what this entails, I have not run the setup yet, but I hope for the best this time around.

Set up SSL? (Recommended)

If you used an https Blog URL and have already pointed your domain to the right place, Ghost-CLI can automatically set up SSL for you using Let’s Encrypt. Alternatively you do this later by running ghost setup ssl at any time.

So if I want to keep it simple I just need to use http in the URL at the “Blog URL” step? That would cause this step not to appear at all? I would like to keep it as simple as possible for now, and skip the SSL part.

Enter your email
SSL certification setup requires an email address so that you can be kept informed if there is any issue with your certificate, including during renewal.

Does this have to be a valid e-mail address? Should I decide to setup SSL later on…

Nginx configuration already found for this url. Skipping Nginx setup.
:information_source: Setting up Nginx [skipped]
Nginx setup task was skipped, skipping SSL setup
:information_source: Setting up SSL [skipped]

I think I will start from scratch now.

Hi there,
You need to proxy_pass from nginx to ghost. Have you figured it out yet? If not, let me know. I’ll guide you through it.

I’m installing it right now. I scrapped it and am starting from scratch. I have just done the “ALTER USER” step. Is this “proxy_pass” step not done for me automatically? Feel free to chip in, because I know nothing about that part.

# To set a password, run
sudo mysql

# Now update your user with this password
# Replace 'password' with your password, but keep the quote marks!
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

# Then exit MySQL

# and login to your Ubuntu user again
su - <user>

If I may ask, why does it say in the comment “and login to your Ubuntu user again”? What is my “Ubuntu user”? The root user on the server? And if I am supposed to login to my “Ubuntu user again”, then with what user was I supposed to run the previous commands with such as sudo mysql? With the root user? I’m just confused about this.

Also, from what I have gathered, the “root” in this context is referring to a MySQL user account named “root”? This is different from the “root” system account on the machine?

Why is this step needed?

If you’re running Ubuntu 18.04, a password is required to ensure MySQL is compatible with Ghost-CLI . This requires a few extra steps!

If I understand this correctly, this is because the MySQL root account on Ubuntu 18.04 has no password by default?

Enter your blog URL: (http://localhost:2368)

Do I have to specify a port at this step, like in the default value above where instead of I type in Should it not be port 80?

? Enter your MySQL hostname: localhost
? Enter your MySQL username: root
? Enter your MySQL password: [hidden]
? Enter your Ghost database name: ghost_prod
✔ Configuring Ghost
✔ Setting up instance
+ sudo useradd --system --user-group ghost
? Sudo Password [hidden]
? Sudo Password [hidden]
? Sudo Password [hidden]
+ sudo chown -R ghost:ghost /var/www/ghost/content
✔ Setting up "ghost" system user
? Do you wish to set up "ghost" mysql user? (Y/n)

When asked for “Sudo Password”, that would be the local user account that’s created at the beginning?

? Do you wish to set up "ghost" mysql user? Yes
✔ Setting up "ghost" mysql user
? Do you wish to set up Nginx? (Y/n)

So now I have one system user called “ghost” and a dedicated MySQL user called “ghost”? What are their passwords?

Now that I think of it… the “Sudo Password” was prompting me to set a password, not to provide an existing one? If so, then the “ghost” system user password is whatever I typed in there, and the “ghost” MySQL user password is the same as that one?

? Do you wish to set up Nginx? Yes
✔ Creating nginx config file at /var/www/ghost/system/files/
+ sudo ln -sf /var/www/ghost/system/files/ /etc/nginx/sites-available/
+ sudo ln -sf /etc/nginx/sites-available/ /etc/nginx/sites-enabled/
+ sudo nginx -s reload
✔ Setting up Nginx
? Do you wish to set up SSL? No
ℹ Setting up SSL [skipped]
? Do you wish to set up Systemd? Yes
✔ Creating systemd service file at /var/www/ghost/system/files/ghost_my-site.service
+ sudo ln -sf /var/www/ghost/system/files/ghost_my-site.service /lib/systemd/system/ghost_my-site.service
+ sudo systemctl daemon-reload
✔ Setting up Systemd
+ sudo systemctl is-active ghost_my-site
? Do you want to start Ghost? Yes
+ sudo systemctl start ghost_my-site
+ sudo systemctl is-enabled ghost_my-site
+ sudo systemctl enable ghost_my-site --quiet
✔ Starting Ghost

Ghost uses direct mail by default. To set up an alternative email method read our docs at


Ghost was installed successfully! To complete setup of your publication, visit:

And of course I’m back at square one. And the URL above ( just returns a 404.

The good thing is I can do this blind-folded now. Not that it would help, but it has been a good exercise nevertheless.

1 Like

I had a look at this very outdated (2013) community tutorial on Digital Ocean on how to configure NGINX and the “proxy_pass” parameter for Ghost.

This is the parameter the author (Alex LaFroscia) used.


I can see that he used the same basic principles with symbolic links, linking configuration files from the NGINX “sites-available” to “sites-enabled” folder.

ln -s sites-available/ghost sites-enabled/ghost

He named the configuration file “ghost”. The setup script I used did basically the same thing, it was just not the same configuration file path and name. You can see it below.

+ sudo ln -sf /var/www/ghost/system/files/ /etc/nginx/sites-available/
+ sudo ln -sf /etc/nginx/sites-available/ /etc/nginx/sites-enabled/

This is what Alex had in his ghost configuration file.

server {
    server_name *your-domain-name*;
    access_log /var/log/nginx/*your-domain-name*.log;

    location / {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header HOST $http_host;
        proxy_set_header X-NginX-Proxy true;

        proxy_redirect off;

I would show you mine, but it looks like I inadvertently deleted it. But as I recall it was pretty much configured the same way, it’s just that for example the “listen” and “server_name” parameters were defined using a different notation regarding port number specification and server name. I don’t recall what “proxy_pass” was set to. So I guess I will have to roll this back and do it again. Thankfully I have a snapshot.

1 Like

I am still beating this horse, but it won’t run. :horse: It might be dead…

Setting up NGINX manually is possible, but why would you choose a hard life?

Well of course! Because automatic setup is much easier?! :joy:

I actually took this problem by some experts on IRC and they all agree that everything looks perfectly normal with my configuration file. But what do they know?! Really?! The whole problem with the Internet today is that it’s full of false, outdated information and badly informed people who pass their own misconceptions onto other people. Just telling like I see it.

How is this supposed to work anyway? I need to understand the expected behavior. I mean…

Ghost was installed successfully! To complete setup of your publication, visit:

Notice! It’s not “” but “”. Also note “to complete setup”. So the installation is completed, but the setup is not? So I take this to mean that I have to go to the “/ghost” URL in a web browser, where I would then probably prompted to create an admin account if one is not created already, add more users to the setup and so on. That would be great… if it worked. Because “” returns a 404 error (page not found). And yet…

✔ Setting up Nginx

Really? Is it really done and set up correctly? According to the setup log it’s perfectly fine now, with a little check mark and everything. All I gotta do now is go to and finish up Ghost setup, because the Nginx setup is already done (and working?).

This Lucky ain’t so lucky…

How old is that setup guide you guys have on display? There is no date of when it was published? But I can see from the page preview on the forum (it picked it up somehow?) that it is from “1 Oct 18”.

If that’s correct, then it’s from 2018 and it’s 2 years old. That’s almost a decade in information age!

Also, is this an original piece? I have found one blog post on another site that’s nearly identical in terms of command lines used for installation, and it predates your setup guide by nearly 1 year (2 Oct 17). Obviously the “ALTER USER” part of it is missing, but that’s normal, since it only came as a requirement with Ubuntu 18.04 LTS (according to your guide).

After 3 days of struggle and frustration, I am finally at Hogwarts. Chasing ghosts! :ghost:

I had some additional hurdles to overcome…

Blocked by X-Frame-Options Policy

Because of the way my current hosting provider and domain name registrar wraps my site in a frame set, the admin panel was inaccessible. So I had to make some changes with nasty side effects, in order to access the admin panel at /ghost and finish the setup.

This introduced double slash // in the URL which results in a… dead ghost?..

So we meet again! Long time, no see.

I’m here just to let you guys know that I have my own Ghost instance up and running now, on my own server, using a really cool .io domain name that I own, so you know there is no stupid double slash // breaking my URL, and since my site is no longer wrapped in a frame set by the hosting provider I no longer run into the “X-Frame-Options Policy” problem (it’s one of the first things I fixed).

Also, since I can set my own DNS records now I have been able to set up SSL with Let’s Encrypt so that I can sign in to my admin panel securely. I even took time to configure my mail settings so that I can send real e-mails for verifying new accounts rather than hacktivating them in the back-end.

Yeah, you can use a fake e-mail address, not a problem. But it’s not recommended! A valid e-mail address has the benefit of you getting notified in case auto-renewal fails for some reason, for example. The address is not published publicly in any registry that I’ve been able to find, if anyone it’s Let’s Encrypt alone that has the e-mail address on record and it’s usually used for sending you important notifications regarding your SSL certificate. So use a valid e-mail address or a fake one, it’s up to you.

No need to do any such thing! At least not in my case. I have tried and tested many things to get this working, but I am pretty sure that the solution lies in removing the default configuration files for Nginx, so that only the newly generated configuration files for Ghost are in their respective location. I did not remove my default configuration files, I just moved them up the folder tree, just in case I need to review them or restore them later (the kind of thinking everyone needs to develop).

mv /etc/nginx/sites-available/default ..
mv /etc/nginx/sites-enabled/default.enabled ..

These are the files I now have in each folder.


This I think is the missing piece of information from the Ghost installation guide.

It’s also needless to say that you have to clear the cache from your web browser. I failed to do that the first few rounds.

Happy ghosting!