Setup a Ghost Blog with a Domain Name using Docker and Nginx
11 min read

Setup a Ghost Blog with a Domain Name using Docker and Nginx

Setup a Ghost Blog with a Domain Name using Docker and Nginx

A little background

Scroll down a bit to get to the actual tutorial.

This was quite an adventure. After a few weeks on and off working on this project, I've finally manage to finish it. My goal was to move my two blogs into one server to make it easier to manage, maintain and keep running. Besides that requirement, I wanted to try to build and host demos of proof-of-concepts of things I want to learn.

If I hosted all that in one standard linux box, it would probably be a nightmare to maintain and keep track off. It's a side project after all, I want to reduce my frustrations. It's supposed to be a fun sandbox after work.

The Cons of an Enthusiast

I love to read about what's the current trend in the software industry or a new gadget, a new service, a new app or discovering new and better ways of doing old things. I've been an early adopter and power user to a lot of services. The unfortunate consequence to that is it can get quite daunting.

As an example, I have a few domains krisviceral.com, krisviceral.me and helloima.ninja (nothing here yet). Why? Well.. that's another story for another day. I've used my .com domain to host my primary blog on wordpress.com. I eventually tried early versions of Ghost for my .me domain. That in itself is already a mess to maintain. I had a DNS provider, where you buy your domains, which routed my domain to two different services. One[1] was easy to use but wasn't fun, the other[2] had given me a lot of freedom and flexibility.

Besides the mess, it would start to get expensive to host all these different things under different services. It would get even more expensive once I try hosting applications. I was looking for the best solution for my situation. After quite a bit of searching and luck, I've found the answer.

Docker.

Docker

Docker
This is a short snippet on docker from my last post.

Docker is an open-source project that automates the deployment of applications inside software containers, by providing an additional layer of abstraction and automation of operating-system-level virtualization on Linux...(Wikipedia)

In computer-science layman's terms, it's a server that can have multiple servers on it. It does it efficiently so that your common resources are effectively used and shared. You can mix and match and run whatever you want on a single machine. You can have a "container" (or think of it as a server) running an application, another hosting a database and another one running a blog. All those containers can have different operating system, allocations, configurations and so on.

I can learn this new fangled thing, host my old stuff, host new things, make it easier to maintain and save while doing it. It actually sounds too good to be true.

What's the catch?

Yes, there are. "Aha! I knew it." You say. There must be at least one. The first and probably the only real one is price. By price, I mean it's not free. To the college students or people not used to paying for things on the internet, it can be offputting to give it a go. It's pretty inexpensive though if you compare it to other setups.

The other con to this whole thing is time. I spent quite a bit of time on it but it's mostly trying to figure out what should I use, how do I use it, how it works, failing to do things correctly and fixing things. That's why I've written this post so that you don't have to waste as much hours as I did.


Actual Tutorial Starts Here

Disclaimer 1: Before you go trying this out just some words of caution. This guide is probably not perfect so there might be some missing things that I've forgot to put (send me a note for feedback). I like to make in-depth tutorials as much as I can because when you are learning something new the last thing you want is to read a tutorial and find out that some key info was missing. (But there are a lot of subtopics here that I hope you know what to do). Anyway, it may require some messing around from your side if some things don't work as expected.

Disclaimer 2: This tutorial is actually piecing together other great tutorials (for different topics). I will put links to those tutorials. I hope I didn't forget any.

Disclaimer 3: I am in no way an expert at these technologies yet. If there is a better way to do things, let me know. Send me a note if you find something off. TheNameOnTheURL [at] gmail.com.

What you need

  • Domain Name
  • A .com, .io, .supplies, .band or even a .pizza
  • Get it from NameCheap, Gandi, Name or the million other domain name registrar
  • Something like $5-15 a year
  • DigitalOcean Account
  • We'll rent a server or vps from them
  • From $5 a month
  • Basic Unix knowledge
  • Or you are damn good at googling
  • Some basic understanding of virtual machines
  • Time
  • Patience

Overview and General Steps

Before diving in to the specific steps, if you are like me, you may want to know the big picture first. It'll save you some scrolling and thinking.

diagram of layers

  1. Get a domain name (not covered)
  2. Get a server with Docker
  3. Get Docker images
  4. Setup Ghost on Docker
  5. Map your Domain to The Server

DigitalOcean and Getting a Server with Docker

You can decide to use someone else to rent a server from but I recommend DigitalOcean for the price, ease-of-use and the number of tutorials they have. That's a pretty good winning combo.

First thing is to register to the site. You can use my affiliate code by clicking here. If you are cost-conscious, just google for some coupon or promo codes, you can normally get 1-2 months free. If you've read this far, you can probably figure out how to register.

Once you've registered, you'll see a large Create Droplet button on the navbar. You should see something like the screenshot below.

Screenshot of form

Step 1: The Minor Details

Pretty self-explanatory so far. Add a name to your server or droplet. Choose a configuration. For a few small apps and not too popular blog, the smallest server might fit your needs. Don't worry if you don't know how much you need. You can turn off the server and choose a more beefy configuration later. After that, select a region closest to you. This is where the server will be hosted.

You can choose to add some things like IPv6 and things like that if you so choose. I recommend the backup option because you should always have a backup. Plus, if you mess anything up, you can revert to a previous server instance. It takes a snapshot of your server at a particular date/time (or you can manually create one). This is useful if you aren't a master of the command line.

Step 2: Build a Server in 2 mins or less

Screenshot server image
I wanted to separate this into a new step because it technically is. It takes like 2 seconds though. Select Applications from the tab in the select image, find Docker {ver number} on 14.04. The 14.04
is the current latest long-term release Ubuntu server. The Docker version is probably the latest one. As an optional step, you can add ssh keys. I won't cover it but that would be a good thing to do.

Hit the create droplet button, wait for 2 mins and you are done. You have a shiny[3] new server.


Configuring Docker

Now that you have a server, we can start playing around with it. If you didn't configure ssh, then you get a password emailed to you.

First thing that you should do

  1. Log in to the server with the password
  2. Change your password
  3. Create a user
  4. Remember this password somehow

After that, remote connect to your server. If you aren't familiar, download Putty or simply go to your droplet from the DigitalOcean site and check out the access. You'll have a terminal available. Handy.
screenshot console access

Some Docker Basic Commands

Before going further, here's some basic commands that we'll be using. Check back here for reference. Some of the terminology will be explained later. Full guide here.

Check if Docker is installed, get some basic details of the docker installation.
sudo docker info

Download an image
sudo docker pull [image-name]

Check the running container
sudo docker ps # Lists only running containers sudo docker ps -a # Lists all containers

Create a container
sudo docker run [some parameters]

Start a running container
sudo docker start [container name] #

Stop a running container
sudo docker stop [container name]

Delete a container
sudo docker rm [container name]

Step 1: Download Images

Now the first step to getting our docker setup configured is to download an image. A docker image, for our intents and purposes, is the stack or bundle of software we want to be installed. It's like a server with all the software configured for us. Some examples of images would be:

  • Ubuntu image - a system just running Ubuntu
  • LAMP image - a (L)inux system with (A)pache, (M)ySQL and (P)HP
  • Ghost image - Nginx, Node JS, Ghost

We pull this from the docker repository. You can check out what's available from the hub by clicking here. For what we need, we'll download the Ghost image.

Download the ghost image
sudo docker pull [ghost]

Step 2: Create a new Container

Now that we have an image, we can create a new docker container. A container is a running instance of an image. So for us a container would be a server running ghost.

To run our ideal Ghost setup:
sudo docker run --name [container alias] -p [custom port]:2368 -d -v /data/ghost/[a folder path]:/var/lib/ghost ghost

diagram of layers
Now, you can run a docker container in a number of ways. The most basic being just a container without outside access to the web. We don't want that. What we want is the server to have access to the web. Also, like a typical computer it would have it's own storage. We don't want that. Since we want to be able to start, stop and remove containers at will, that would mess up our ghost database. You don't want to be reimporting all the themes and posts all the time. We can separate ghost's important folders outside of the container and into our actual server. This way even if we stop the server and let's say download a new ghost image (a newer version), we can just create a new container and it would magically still have all our data.

Explaining the custom parameters
  • [container alias] - the name of your super awesome blog
  • [custom port] - what the host port would be (for example server.com:port), the port for http is 80. It's okay if it's not 80, we'll map it with nginx later
  • [folder path] - this will be a folder inside docker where all the data for the container (/var/lib/ghost) would be reside. The container will save it outside of the container and into the docker server.

Note: You can use docker volumes instead of the folder path but I haven't tried volumes yet.

If you try sudo docker ps, you should be seeing your new container is running.

Step 2: Check out your new Ghost Blog

Login to your-server-ip:[custom port you specified] and you should now see that Ghost is running!


Configuring Ghost

I won't discuss this too much since there are a lot of good tutorials out there. I just want to list out some things you can do to improve your blog. You can google them further.

  • Add domain name to config.js
  • Config is a json file where you can change some parameters. You'll see a parameter for site name
  • Ghost uses this domain name for hyperlinks in the site
  • Obviously, it won't work yet if you haven't mapped domains yet (next section)
  • You can either use an editor right in the docker instance (like nano or vim) or just ftp copy to your computer, modify and ftp it back.
  • Configure a mailer - Useful for sending those "reset password" emails
  • http://support.ghost.org/mail/
  • Customize a theme - download a theme from ghost marketplace and customize it
  • Requires a notepad, bit of css knowledge
  • Run Ghost in Production mode
  • By default, it's set to run in development mode

Some nifty commands (when you are inside the container)

Start/Stop Ghost - when you change configurations, reload ghost
sudo service ghost start sudo service ghost stop


Mapping a Domain and Configuring Nginx

Almost there! We have a domain name, a server and a ghost container. Now, we just need to map the domain. I don't think anybody will visit your site if it's an ip address and a port. Have you ever gone to google with an ip-address?

Step 1: Map the Domain to Digital Ocean

If you don't understand how domain names work, it will be helpful to do a quick google or check out some youtube videos on it. In a quick summary, once you type a URL in a browser, a bunch of computers determine to what ip address I should go to. Currently, your domain is not mapped to anything so if you type it in the browser, the internet breaks and doesn't know what to do. What you'll do is go to you domain name registrar (where you bought your domain), find the setting for domain mapping and map it to the servers on DigitalOcean.

How do you do this? Well here is a Guide to Mapping a domain to DigitalOcean.

Note: Don't forget to add a CNAME www for your domain. Most people type www.domain.com so if you forgot this people can't access your site.

Step 2: Mapping the domain to your Ghost Instance with Nginx

After you finish the step above, what is happening now is that when you type your-awesome-domain.com, it reaches your server but it doesn't know what to do. What we'll do know is to install Nginx web server. What'll happen is that we'll create a file telling nginx to go to ip-address:[your custom port] every time someone we receive a request for your-awesome-domain.com

Login to your Docker machine, and we'll install Nginx using the command below.
sudo apt-get install nginx

Start nginx
sudo service nginx start

Note: For more info on installing Nginx, click here

Now that we have nginx installed and running, let's configure the mapping. Create a conf file using the command below. It uses nano text editor so feel free to switch to whatever you prefer.
sudo nano /etc/nginx/conf.d/[your domain].conf

Place this snippet.

server {
    listen 80;
    server_name your-domain.com;
    
    location / {
        proxy_pass http://localhost:{YOUR_PORT};
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
   }
}

A few notes on this snippet. This is originally from this tutorial. You can add multiple mappings if you have multiple domains and you want to map them to different instances (like what I'm doing to http://krisviceral.com and http://krisviceral.me). Simply, add another snippet (with no semi-colon) and change the details. You can also add a lot of extra configuration like log files and other things. If you have a large amount of server mappings or you may want to modify server_names_hash_bucket_size.

Read more on how it works: Nginx Server Names and
Nginx Request Processing.


Conclusion

diagram of layers

How it all works

  1. User types in URL
  2. Domain name registry figures out the ip-address to pass to
  3. Request is received in server
  4. Nginx figures out what to do (using your conf file)
  5. It points it to a port in localhost
  6. Docker routes this request to the app (Ghost)
  7. User is Happy.

Congratulations! You now have a Ghost blog hosted on docker! I hope you actually were able to follow the tutorial. If you are stuck or would like to send feedback, send me a note at theNameOnTheURL[at]gmail.com or check out the contact page.


Footnotes

  1. Hosting on wordpress.com ↩︎

  2. Self-Hosting with Digital Ocean ↩︎

  3. May not be actually shiny ↩︎