Setting up Ghost CMS on a VPS

A fantastic use for a VPS instances is to host web-servers and blogs. As cloud servers are pretty inexpensive these days, it might be time to test them. Ghost is targeting hosted and self-hosted blogs.

Setting up Ghost CMS on a VPS
Photo by Kaffee Meister / Unsplash
This is a generic discussion not a vendor specific guide.

Pre-requisites for setting up Ghost CMS.

  • have a site, the FQDN will be used by ghost and Let's Encrypt
  • set an A-record to point to the cloud server's IPv4
  • set an AAAA-record to point to the cloud server's IPv6 address, if possible
  • an email address for registering with Let's Encrypt and receiving notifications about your certificates
  • a strong SSH-key for the user
  • create a small cloud server on your preferred provider or a VM on your Proxmox, 1 core and 2M RAM is plenty for a basic blog, minimum is 512k for RAM and SWAP and 0.1 core. The guide is tested with Ubuntu 22.04. (When 24.04 is more mature, I will revisit this article, after August when .1 is released.

In this example, the following are assumed

  • <user> = Joe, just a fake name
  • <password> = MySuperSecretPassword,
    you should use something safe and not a joke like this
  • <sitename> = example, from the FQDN you own and want to use
  • Ghost <dbname> = ghostdb; you could be more creative

SSH-keys

Secure Shell (SSH) is a protocol used on Linux to access and manage servers remotely. This allows for the remote execution of commands, file transfers, and other tasks.

Creating new users with specific permissions is an important aspect of server management. It’s crucial to understand the risks of granting root access, which should only be given to trusted users and monitored to maintain security.

Generate an SSH key pair with the ssh-keygen command

ssh-keygen -t ed25519 -C “[email protected]” -f ~/.ssh/mykeys/vps_key -P MySuperSecretPassword
  • -C “[email protected]
    A comment is appended to the end of the public key file to easily identify it.
    Normally, an email address is used for the comment, but use whatever works best for you.
  • -f ~/.ssh/mykeys/vps_key
    Filename of the private key file, if you choose not to use the default name.
    Two files are created, the private key vps_key and the corresponding public key vps_key.pub is generated in the same directory. The directory must exist.
  • -P "MySuperSecretPassword"
    By default, ⁣ssh-keygen will prompt for the passphrase before creating the key pairs. But we can also assign passphrase with using -P <your_password>

Change the passphrase of your private key

  • Use ssh-keygen with -p will prompt for the location of the private key file
    • Next provide the existing passphrase of your private key
    • If the provided passphrase is correct, you will get the prompt to assign a new passphrase to your existing private key
  • Use -P and -N to give a passphrase
ssh-keygen -p -f ~/.ssh/id_ed25519 -P OLDpassword -N NewSuperSecretPasswd
  • Show the comment, ssh-keygen -l, will output
Enter file in which the key is (/Users/joe/.ssh/id_ed25519): akamai-01
256 SHA256:SrpdXpA1bv8QXNv0ky+cmAsY2OTEGkuIo+GZkj/2zWQ “[email protected] at akamai-01” (ED25519)
  • Change the comment, ssh-keygen -c, will output
Enter file in which the key is (/Users/joe/.ssh/id_ed25519): akamai-01
Key now has comment 'This is for droplet9.example.com'
Enter new comment: 'This is for hetzner-8.example.com'
The comment in your key file has changed.

Copying the key to a server

ssh-copy-id -i $HOME/.ssh/id_rsa.pub <user>@<remote-host-IP-or-FQDN>
ssh-copy-id -i $HOME/.ssh/id_rsa.pub <joe>@<ubuntu-4.example.com>

Will ask for the password at login.

The New Server

Create a VPS Ubuntu server on your favorite cloud provider.

Login as root to thew new Ubuntu server

ssh [email protected]

Server set up with an SSH key

If you already set up your server with an SSH key and it isn't your default key you need to tell your machine where to find it

ssh -i /path/to/ssh/key [email protected]

Hardening the server

After you created the virtual server, login as root and start setting up the basics.

It's good practice not to use root for dealing with any server over the internet, and it's even recommended to do so in your home lab.

Create a new user

The user also needs a long password.
If everything is ok and the new user owns the .ssh folder, you can continue.
Alternatively, we copy the key created/given to root at the creation of the virtual server to the file authorized_keys. And change the files privileges to 600.

adduser <user>
usermod -aG sudo <user>
cd /home/user
chown -R <user>:<user> .ssh
ls -Alh

Change keyboard and locales

dpkg-reconfigure keyboard-configuration
dpkg-reconfigure locales

Switch to the new user

Now log out of the root user with the following command or [CTRL]+d

[email protected]:~$  exit

Afterwards we can log in with our new user

ssh <user>@10.10.10.1

Now that we are logged in as a non-root user we need to use sudo in front of all administrative commands.

Secure the SSH-server

Edit the file and inhibit root login and force use of ssh-keys.

sudo nano /etc/ssh/sshd_config
  • Change to no if it's yes PermitRootLogin no
  • Change to no if it's yes PasswordAuthentication no

Now restart the services

  • sudo systemctl restart sshd.service

Check for success

  • sudo systemctl status sshd
  • sudo systemctl status ssh

Enable the firewall

After creating the non-root user we want to enable a firewall.
We will use the Uncomplicated FireWall ufw.

List all available applications

We want to list all available applications which we can register with the firewall.

List them with the following command:

sudo ufw app list

This will be the output:

Available applications:
  OpenSSH

Allow OpenSSH

As we want to use SSH for logins, we need to enable it before we activate the firewall!

sudo ufw allow OpenSSH

Enable the firewall

Now, that the firewall allows SSH connections we can activate it

sudo ufw enable

Then check for success

ufw status
Status: active

To                         Action      From
--                         ------      ----
OpenSSH                    ALLOW       Anywhere
OpenSSH (v6)               ALLOW       Anywhere (v6)

The firewall now blocks all connections except SSH.

If we install new applications we need to activate them.
Otherwise their connection will be blocked by the firewall.

Add SSH keys

Copy the public key you created from your local machine to your server with the following command:

cat ~/.ssh/ssh_key.pub | ssh <user>@10.10.10.1 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
  • Replace ~/.ssh/ssh_key.pub with the path to your public SSH key.
  • Replace <user> with your new username
  • Replace 10.10.10.1 with the actual IP or FQDN of your server.

Alternative

List the key cat /root/./ssh/authorized_keys and copy the whole key.

Go to the users .ssh directory.

mkdir -p ~/.ssh
chmod 700 ~/.ssh
cd /users/<user>/.ssh
nano ~/.ssh/authorized_keys

Drop in the copied key and save [ctrl]+0 and exit [ctrl]+x

Secure the file with chmod 600 ~/.ssh/authorized_keys

Edit the sshd config

Now, that we use SSH for our login we need to deactivate password based login and the login for the root user.

For this we will edit the file /etc/ssh/sshd_config:

sudo nano /etc/ssh/sshd_config

Deactivate password based login

Within the /etc/ssh/sshd_config-file locate the following line

PasswordAuthentication yes

And change it to

PasswordAuthentication no

Disable root login

Within the file locate the following line:

PermitRootLogin yes

And change it to

PermitRootLogin no

Restart sshd service

Save [ctrl]+o and close [ctrl]+x the editor and then restart the sshd service

sudo systemctl restart ssh

Upgrade the server

Run sudo apt update && sudo apt-get dist-upgrade -y

Install Ghost CMS

Now we will set up a Ghost instance directly on the server using NGINX, MariaDB, NVM, Node.js and Ghost-CLI. If you prefer MySQL, use it.

Install NGINX

sudo apt-get install nginx

Set up the firewall to let ports 80 and 443 in

sudo ufw allow 'Nginx Full'

Check for success

sudo ufw status

Install and setup MariaDB

As <user> we use the newly created one and a really long and complicated password as the <password> for the database.

sudo apt install mariadb-server -y
sudo mysql
CREATE USER '<user>'@localhost IDENTIFIED BY '<password>';
CREATE DATABASE ghostdb;
GRANT ALL PRIVILEGES ON ghostdb.* TO '<user>'@localhost IDENTIFIED BY '<password>';
FLUSH PRIVILEGES;

Install Node.js Using Node Version Manager

You should always read any script before using them, especially piping (|) into bash is really dangerous. If you do not trust or understand the script, do not use it.
Link to the script in References.

curl -o- https://raw.githubusercontent.com/nvm/v0.39.7/install.sh | bash
exec $SHELL
# Test for success
nvm 
# Install node.js
nvm install --lts
# Check for success
node -v
nvm -v

Note, curl -o- will fetch the URL and send the output to stdout (typically the terminal).

To update use npm install -g [email protected] or what the version is to be.

Install Ghost-CLI

npm install ghost-cli@latet -g

We set it as global with the -g flag.

Create a directory for the site

  • Create a directory <sitename>
  • Change directory owner to our <user>
  • Set the correct permissions
  • go to the site for installation
sudo mkdir -p /var/www/example

sudo chown joe:joe /var/www/example

sudo chmod 775 /var/www/example

cd /var/www/example

And finally install ghost

Install Ghost in the site directory

ghost install

You should now have two folders: <sitename> and html.

You will need to answer the following questions:

  • Enter your blog URL: https://www.<siteneme>.com
  • Enter your MySQL hostname: localhost
  • Enter your MySQL username: <user>
  • Enter your MySQL password: <password>
  • Enter your Ghost database name: ghostdb
  • Do you wish to set up Nginx?: Y/n - y (or Enter)
  • Do you wish to set up SSL?: Y/n - y (or Enter)
    • Enter your email (For SSL Certificate):
  • Do you wish to set up Systems? Y/n - y (or Enter)
  • Do you wish to start Ghost? Y/n - y (or Enter)

Use some more features

A complete guide to code snippets
Developers write code. Some developers write about writing code. But when they try to share that code on the web, everything that makes code more readable – like formatting and syntax highlighting – is gone!
highlight.js
The Internet’s favorite JavaScript syntax highlighter supporting Node.js and the web.

Reverse Proxy

You should definitely install one!

The reverse proxy service serves as the front-end for all incoming client requests and routes them to the back-end servers (web, database, or other).

Using a Reverse Proxy gives you several benefits:

  • Load Balancing – they can improve performance, scalability, and reliability. A reverse proxy server can help your backend servers by dividing client requests between several servers. This helps them work faster and use less resources. If a server goes down, the load balancer redirects traffic to the remaining servers.
  • Improved Security – By intercepting requests heading for your backend servers, a reverse proxy server protects their identities and acts as an additional defense against security attacks, clients will not have information about our backend servers, so there is no way any malicious client can access them directly to exploit any vulnerabilities. Furthermore, it ensures that multiple servers can be accessed from a single record locator or URL, irrespective of the configuration of your local area network.
  • Monitoring and logging - A Reverse proxy has the potential to enhance security by providing enterprises with a means to monitor and record traffic flowing through their network.
  • Better Performance – A reverse proxy is capable of storing pre-rendered versions of pages to enhance page loading times. It works by storing content received from proxied server responses and then using it to respond to clients without having to contact the proxied server for the same content every time.
  • SSL Termination – A reverse proxy can act as an SSL endpoint for connections with the clients. It will handle and decrypt incoming SSL connections and encrypt the proxied server’s responses.
  • Different Websites into a Single URL Space and URL rewriting - With a reverse proxy server, the URLs can be automatically changed before they are passed to the backend servers. With a reverse proxy, you can send a single URL to many different components. If someone clicks on your URL, it will look like they are going to another page on the website. Each page in that URL may be connecting to a different backend service.
  1. Traefik is an open-source Edge Router that makes publishing your services a fun and easy experience. It receives requests on behalf of your system and finds out which components are responsible for handling them. Traefik (pronounced traffic) is a modern HTTP reverse proxy, and load balancer written in Go that makes deploying microservices easy. Traefik integrates with your existing infrastructure components (Docker, Swarm mode, Kubernetes, Consul, Etcd, Rancher v2, Amazon ECS, ...) and configures itself automatically and dynamically. Pointing Traefik at your orchestrator should be the only configuration step you need. Link to the documentation.
  2. Nginx is a high-performance, open-source HTTP and reverse proxy server. It is known for its stability, robustness, and scalability, and is commonly used for load balancing, reverse proxying, and caching. Link to the documentation.
  3. Caddy is a modern, open-source web server and reverse proxy server that is designed to be easy to use and efficient. It supports HTTP/2, TLS encryption, automatic HTTPS, and many other features. It is written in Go and is an extensible platform to serve your sites, services, and apps. Caddy is a server of servers, but most people use it as a web server or proxy. Link to the documentation.
  4. Apache is a widely used open-source web server and reverse proxy server. It is renowned for its adaptability and can be enhanced by incorporating Apache modules to include features such as proxying and load balancing. Link to the documentation.
  5. HAProxy is a fast and reliable open-source TCP/HTTP load balancer and reverse proxy server. It is known for its flexibility, ease of use, and high availability. It has features that are commonly used in HAProxy, but not necessarily present in other load balancers. Link to the documentation.
  6. Envoy Proxy is a high-performance, open-source edge, and service proxy server designed for modern, cloud-native architectures. It is designed to be massively scalable, extensible, and customizable, and supports features such as service discovery, load balancing, and rate limiting. Link to the documentation.
  7. Sōzu is a reverse proxy for load balancing, written in Rust. Its main job is to balance inbound requests across two or more clusters' backends to spread the load. It serves as a termination point for SSL sessions. So the workload of dealing with the encryption is offloaded from the backend. It can protect the backends by preventing direct access from the network. It returns some metrics related to the traffic between clients and backends clusters behind it. Link to the documentation



References

Ghost [1] NVM [2] SSH [3] ufw [4] Reverse Proxy [5] PRISM [6]


  1. Ghost homepage ↩︎

  2. Node Version Manager GitHub ↩︎

  3. Secure Shell - SSH man page, ssh-keygen man page, article on GitHub ↩︎

  4. Uncomplicated firewall homepage, Git, article Digitalocean, article Ubuntu ↩︎

  5. Reverse proxy wikipedia ↩︎

  6. PRISM homepage ↩︎