Running Ghost on Scaleway node.js instance with HTTPS
- Spin up new Node.js instance on Scaleway
- SSH to your instance and install unzip
- Create folders and install Ghost
- Set up NGINX to serve through port 80
- Set up forever and run Ghost under non-root user
- Setting up HTTPS with Letsencrypt and non-WWW redirect
- Auto-renewal of Letsencrypt certificate.
Unix commands used in this tutorial: cd, pwd, mkdir, touch, apt-get, wget, vim.
When going trough the tutorial, don’t just copy paste the commands, try to understand them and make sure you are using correct paths when executing. Replace primoz.xyz
with your own domain name in all commands and config files.
Spin up new Node.js instance on Scaleway
To connect to your instance with SSH, you will need to add your public key to Scaleway, just like for any other cloud provider. Generating the key in not in the scope of this tutorial.
Open your favorite terminal and let’s get started.
On macOS by default, the key generated with ssh-keygen is saved to file id_rsa id .ssh folder. To copy the key to your clipboard use this command and then add your key to credentials to your Scaleway profile. (https://cloud.scaleway.com/#/credentials)
pbcopy < ~/.ssh/id_rsa.pub
On Scaleway select create server and under ImageHub select Node.js. Click create and you should end up on you server summary page. You will probably need to wait a couple of minutes for your instance to boot up. Find your instance public IP and SSH from your local computer to that IP.
BTW; Scaleway also provides Ghost image, which you can use instead of Node.js, but I have no idea what you get with it.
SSH to your instance and install unzip
ssh root@51.15.137.165
If you are connecting to this IP for the first time you will need to add the ECDSA key to the list of knowns host. You will see a screen with some stats about the instance you just connected to.
When you first login in you end up in /root
user directory. You can check this with pwd
command.
root@node-web-server:~# pwd
/root
First, let’s make sure package manager is up to date and install unzip
package that we will use to unpack our Ghost installation.
root@node-web-server:~# apt-get update
root@node-web-server:~# apt-get install unzip
Create folders and install Ghost
Ghost.org recommends installing your blog to folder /var/www/
. We can create and move to this folder by executing following commands.
root@node-web-server:~# mkdir /var/www
root@node-web-server:~# cd /var/www
root@node-web-server:~# pwd
Make sure you are in /var/www
directory with pwd
.
I decided to place my blog into another subfolder, because in the future I might run more than one Ghost instance on the same server. I will name the folder ghostprimozxyz
root@node-web-server:/var/www# mkdir ghost_primoz_xyz
root@node-web-server:/var/www# cd ghost_primoz_xyz/
OK. Now with the folder structure in place. Let’s download and unzip Ghost package.
root@node-web-server:/var/www/ghost_primoz_xyz# wget https://ghost.org/zip/ghost-latest.zip
root@node-web-server:/var/www/ghost_primoz_xyz# unzip ghost-latest.zip
This will unzip the content of the page, and your folder should look like this.
root@node-web-server:/var/www/ghost_primoz_xyz# ls
config.example.js core Gruntfile.js LICENSE package.json README.md
content ghost-latest.zip index.js npm-shrinkwrap.json PRIVACY.md
You can now delete ghost-latest.zip
if you like, we don’t need it anymore. All the necessary files are extracted.
Next we need to install all production npm packages. Run the following command. This command will go trough package.json file and install all necessary dependencies needed for the production environment.
root@node-web-server:/var/www/ghost_primoz_xyz# npm install --production
We also need to create Ghost config file by copying the example file in the folder. This command will create a new file called config.js
which will be used by Ghost for configuration. If you mess up this file, you can always bring back the default by making another copy from config.example.js
root@node-web-server:/var/www/ghost_primoz_xyz# cp config.example.js config.js
Use VIM to open config.js and edit production URL property to match your domain name.
Quick VIM refresh: i
and ESC key to switch between insert and command mode. Type :write
and :quit
to save and exit.
root@node-web-server:/var/www/ghost_primoz_xyz# vim config.js
At this point, you could start the server, but the site would not be accessible to anyone through a browser for security reasons. By default, Ghost server is running on port 2368
and Scaleway is not letting us access this port. We will use NGINX to proxy traffic to public port 80.
Set up NGINX to serve through port 80
Let’s start by installing NGINX package.
root@node-web-server:/var/www/ghost_primoz_xyz# apt-get install nginx -y
-y
takes care of automatically answering with yes to all questions asked by command line while installation process.
Let’s remove the default NGINX config next. We don’t need it there confusing us in the future.
root@node-web-server:/var/www/ghost_primoz_xyz# rm /etc/nginx/sites-enabled/default
And then create our own config. You can use and other file name, but make sure you are referencing to correct file while following the tutorial.
root@node-web-server:/var/www/ghost_primoz_xyz# touch /etc/nginx/sites-enabled/ghost-primoz-xyz.conf
Next let’s edit our new config file with the following configuration.
root@node-web-server:/var/www/ghost_primoz_xyz# vim /etc/nginx/sites-enabled/ghost-primoz-xyz.conf
Make sure to change the property server_name
to your own domain. We are not adding www.primoz.xyz here. We will deal with proper www subdomain handling later on. But this is the config that will get you started.
server {
listen 80;
server_name primoz.xyz www.primoz.xyz;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:2368;
}
location ~ /.well-known {
allow all;
root /var/www/letsencrypt;
}
}
When pasting this snippet make sure you don’t miss the first character s
in server. After all the editing and hopeful successfully exiting VIM we can restart NXING.
root@node-web-server:/var/www/ghost_primoz_xyz# service nginx restart
If all went well out site should be accessible by the IP and port 80. But not before starting the Ghost server with
root@node-web-server:/var/www/ghost_primoz_xyz# npm start --production
But let’s not stop here. Shut down the server with ctr+c
. We will finish this by adding npm package forever
and running the server from different unix user. This will have a couple of benefits. Security and you can run more than one Ghost site on the same cloud instance.
Set up forever and run Ghost under non-root user
Check out forever github page for more info. https://github.com/foreverjs/forever
Install it globally with the following command.
root@node-web-server:/var/www/ghost_primoz_xyz# npm install -g forever
Adding new user and giving correct privileges. I chose the name ghost-primoz
for my user. Feel free to use anything else.
root@node-web-server:/var/www/ghost_primoz_xyz# adduser --shell /bin/bash --gecos 'Ghost application for primoz.xyz' ghost-primoz
root@node-web-server:/var/www/ghost_primoz_xyz# chown -R ghost-primoz:ghost-primoz .
Note the dot at the end of the command. This is giving privileges to current directory. Make sure you are using correct path here, you want to give privileges to correct folder.
Switch to ghost-primoz
user by executing:
root@node-web-server:/var/www/ghost_primoz_xyz# su - ghost-primoz
You are now logged in non root account, SWEET. Start Ghost by moving to /var/www/ghost_primoz_xyz
folder and setting NODE_ENV variable to production
and starting forever.
ghost-primoz@node-web-server:/root$# cd /var/www/ghost_primoz_xyz/
ghost-primoz@node-web-server:/var/www/ghost_primoz_xyz$# NODE_ENV=production forever start index.js
You can use forever list, forever stop {id}
to manage forever processes.
Type exit
to logout of ghost-primoz account back to root. Don’t worry your Ghost node process is still up and running.
We are all set up and ready to start with the next section.
Setting up HTTPS with Letsencrypt and non WWW redirect
You need to setup your DNS records to correctly point to your public Scaleway instance IP. Use type A DNC record.
At this point you blog should be accessible by visiting you domain name: http://primoz.xyz in my instance. If you just edited your DNS settings give it a couple of minutes to update.
If we want to take advantage of Certbot’s automatic renewal we have to use --webroot
method for creating the certificate. Because Letsencrypt certificate expire after 90 days we need to automatically renew them. Renewing them will be a lot easier if automated. When creating and renewing the certificate Letsencrypt is checking if you have control of the server and domain. They do this by checking against file in .well-known
folder in the root of your domain.
Download package to root user folder. This is done by running.
root@ghost-web-server:~# cd /root
root@ghost-web-server:~# wget https://dl.eff.org/certbot-auto
root@ghost-web-server:~# chmod a+x certbot-auto
Let’s install it with
root@ghost-web-server:~# ./certbot-auto
Create letsencrypt
folder in /var/www/
. This is where certbot will create temporary file used for verification.
root@node-web-server:~# mkdir /var/www/letsencrypt
Next we need to configure NGINX to serve .wel-known
folder through our domain.
Update NGINX config file to support only https and redirect http to https.
Create the certificate. Use your own domain name. If you used NGINX config from step Set up NGINX to serve through port 80
everything shoudl go smoothly.
root@node-web-server:~# ./certbot-auto certonly --webroot -w /var/www/letsencrypt/ -d primoz.xyz -d www.primoz.xyz
Next we will update our NGIX config to redirect all traffic from http to https and from www.primoz.xyz to primoz.xyz without WWW.
root@ghost-web-server:~# vim /etc/nginx/sites-enabled/ghost-primoz-xyz.conf
Again when copying this config make sure to change servername and all sslcertificate paths.
server {
listen 443 ssl;
server_name primoz.xyz;
ssl_certificate /etc/letsencrypt/live/primoz.xyz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/primoz.xyz/privkey.pem;
access_log /var/log/nginx/primoz.xyz.log;
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:2368;
}
location ~ /.well-known {
allow all;
root /var/www/letsencrypt;
}
}
server {
listen 443 ssl;
server_name www.primoz.xyz;
ssl_certificate /etc/letsencrypt/live/primoz.xyz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/primoz.xyz/privkey.pem;
access_log /var/log/nginx/primoz.xyz.log;
return 301 $scheme://primoz.xyz$request_uri;
}
server {
listen 80;
server_name primoz.xyz www.primoz.xyz;
return 301 https://primoz.xyz$request_uri;
location ~ /.well-known {
allow all;
root /var/www/letsencrypt;
}
}
Restart nginx with:
root@node-web-server:/var/www/ghost_primoz_xyz# service nginx restart
We also need to update our ghost config file with new HTTPS URL.
root@node-web-server:~# vim /var/www/ghost_primoz_xyz/config.js
And change production.url property to https://primoz.xyz
note the s there.
Auto renewal of letsencrypt certificate.
Check if auto renewal works by dry running wit this command.
root@node-web-server:~# ./certbot-auto renew --dry-run
Create file in /etc/cron.houry
root@node-web-server:~# touch /etc/cron.hourly/certboot-auto-renew
root@node-web-server:~# vim /etc/cron.hourly//certboot-auto-renew
And paste this in.
#!/bin/bash
~/certbot-auto renew --quiet --no-self-upgrade
DONE!