Ghost Tips & Tricks #12 / Install Multiple Ghost Installations on DigitalOcean

Hello everyone,

This is issue number #12 of Ghost Tips & Tricks. Brought to you by Aspire Themes. Premium and high-quality Ghost themes.


:raised_hand:t4: Always make sure to backup your droplet before doing any server work.


If you already installed Ghost on DigitalOcean using the Ghost 1-Click App and want to install an additional Ghost instance on the same droplet, this step by step guide is for you.

I assume:

  • You are logged in to your droplet using SSH from the command line

  • Your domain name is ready and connected to the droplet IP

➊ You need the current MySQL host, username, and password.


cd /var/www/ghost/

cat config.production.json

Take notes of them. You will use them later in steps 3 and 5.

➋ Create a website directory, for example, ubud.


cd /var/www/

sudo mkdir ubud

sudo chown ghost-mgr:ghost-mgr ubud/

➌ Login to MySQL and create a new database


sudo mysql -u root -p

CREATE DATABASE ubud;

GRANT ALL PRIVILEGES ON ubud.* to 'ghost'@'localhost' IDENTIFIED BY '7d150600c49e7c5860be7ce03d4f8df67259176738d926ec';

FLUSH PRIVILEGES;

exit

  • ghost → database user

  • localhost → database host

  • 7d150600c49e7c5860be7ce03d4f8df67259176738d926ec → database password

➍ Switch to the ghost-mgr user


sudo -i -u ghost-mgr

➎ Install Ghost and follow the installation steps


cd /var/www/ubud/

ghost install


That’s all that I wanted to share today, and I hope you find this post helpful.

Checkout previous parts of the Ghost Tips & Tricks series:

Also, check out my Ghost Websites Inspiration and Ghost Newsletter series.

Stay safe!

Ahmad

4 Likes

Thank you @ahmadajmi, for yet another very useful post here on the forum.

I’d love to merge the 6 or 7 sites I currently have running on separate droplets into a single larger droplet (to save money) but I assume this will make it easy for people to associate the websites with each other (they’ll all have the same IP address).

I’m not too keen on having all of my websites (including my personal blog, a personal blog I run for a friend, and some professional websites) associated with each other.

If I understand correctly, Cloudflare should be a solution to this problem by only letting people see their shared IP address rather than the IP address of my droplet?

However I’m thinking it would be prudent to have different IP addresses for each website anyway, as a second layer of protection, in case Cloudflare fails or something?

I believe you can have 16 different IPv6 addresses per droplet, so each website could have its own IPv6 address, but looking at this page:

How to Enable Additional IPv6 Addresses :: DigitalOcean Documentation

All 16 of the IPv6 addresses seem to be the same except for the final digit or character. So that doesn’t really help.

If anybody has any insight or suggestions, it would be much appreciated.

I also merged all my Ghost instances (theme demos) in one droplet, and it works fine for me. For Cloudflare and IPv6, I’m afraid I won’t be of much help here, so I hope someone much experienced in this topic would help.

1 Like

Thanks again :slightly_smiling_face:

The problem is that people can use reverse IP address lookup tools like these to get a list of every website sharing an IP.

https://spyse.com/tools/reverse-ip-lookup

I think there are good reasons why people might not want to link their personal blogs, business or other project websites and websites they are running for clients in this way.

But the cost of running multiple droplets really does add up.

1 Like

I really like this tip as this allows combining multiple sites to one.

I would recommend checking out sqlite for DB as the server requirements will reduce dramatically.

I enabled Ghost+Cloudinary with sqlite recently with Traefik reverse proxy and was a breeze.

SqliteDB allowed me to backup the DB in DO volumes which brings the cost of DB to ~5USD(+tax) per year and you might be able to turn off the Droplet if you step into Static Site solutions like Next.js and Gatsby

1 Like

Thanks for these instructions. Everything works up until this step:

GRANT ALL PRIVILEGES ON ubud.* to ‘ghost’@‘localhost’ IDENTIFIED BY ‘7d150600c49e7c5860be7ce03d4f8df67259176738d926ec’;

I obviously use my correct password for my db, but I am getting this error:

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'IDENTIFIED BY ‘xxxxxx’ ’ at line 1
mysql>

What am I missing? Any tips? Thanks!

Not sure. I used the same steps to add new installations a few days ago, and all went fine.

Maybe try a new database name, login and out, and do the same steps again? Perhaps you need to update the droplet or the database version?

My MySQL and Ubuntu versions:

lsb_release -a
mysqladmin -u root -p version

Hi Ahmad, thanks for the great guide!

I managed to install Ghost on the same droplet as another Ghost website but how do I access it?

Obviously that droplet is using has an IP address that I’m already using for the first Ghost installation…

any idea here?

1 Like

By the domain name, the one you use when setting up the second installation. The first and second domain name will point to the same IP.

Hi, @tshaw I ran into the same issue. It seems the IDENTIFIED BY keywords in a GRANT statement are no longer supported in more recent versions of MySQL. IDENTIFIED BY is used to change the password, so unless you need to change the password of your user account, you can safely omit it. Hope this helps or helps anyone else reading later.

Thanks for the great post @ahmadajmi !

1 Like

I have actually tested installing the latest versions, and Ghost is saving properly. During the setup, instead of entering ‘localhost,’ we need to enter the IP address. Additionally, the username and password must be entered manually. Use the following command:

GRANT ALL PRIVILEGES ON ubud.* TO 'ghost_username'@'LOCAL_IP';

Omit the ‘IDENTIFIED BY’ line, as old passwords will be used. To retrieve these details, run cat config.production.json

Please replace ‘ghost_username’ and ‘LOCAL_IP’ with the actual Ghost username and local IP address as needed.


{
  "url": "https://domain",
  "server": {
    "port": 2368,
    "host": "127.0.0.1"
  },
  "database": {
    "client": "mysql",
    "connection": {
      "host": "127.0.0.1",
      "user": "ghostusernameUnique",
      "password": "password",
      "port": 3306,
      "database": "ghost_production"
    }
  },
  "mail": {
    "transport": "Direct"
  },
  "logging": {
    "transports": [
      "file",
      "stdout"
    ]
  },
  "process": "systemd",
  "paths": {
    "contentPath": "/var/www/ghost/content"
  }
}