The open source Ghost content management system is the first I've used that can actually replace WordPress. So, with Halloween on our doorstep, I thought it might be fun to break down a few methods for launching a Ghost blog in the cloud with the help of Docker.
Not sure whether Ghost or Wordpress is right for you? Check out my guide to blogging platforms here.
For the purpose of this tutorial, I'll be using a Digital Ocean virtual machine — what they call a Droplet — but the process only differs slightly if you'd prefer to use Google Cloud Platform, Amazon Web Services, Microsoft Azure, or something else entirely. Many cloud providers even offer free virtual machine (VM) instances that are more than powerful enough to run Ghost.
Prerequisites
Before we get started, this guide assumes you have:
- Purchased a domain — Need one? I recommend Hover.com
- Created a Cloudflare account and added your domain — Optional, if you are using the advanced method. Learn more here.
- Created a Digital Ocean account — Sign up here.
Before you get started, this is a long post with a lot of steps. I recommend grabbing a cup of coffee or tea before you get started.
Table of contents
- Set up your Digital Ocean Droplet
- Install Docker
- Install Ghost:
- Configure your DNS settings
- Set up your Ghost admin account
- Secure your blog with:
- Cloudflare Edge Certificate
- Let's Encrypt SSL Certificate (Advanced mode only)
- Helpful resources if you run into trouble
Creating a virtual machine
In this guide, I'll be breaking down two methods for launching a Ghost blog in the cloud using Docker containers. But, before I get to that, you'll need to create a virtual machine. This might sound daunting, but it's actually quite easy thanks to Digital Ocean's convenient launch wizard.
If you plan to use another cloud provider, create a virtual machine of your liking and skip to Install Docker.
Once you've signed into your account, create a virtual machine — Digital Ocean calls these Droplets. You can do this by clicking "Droplet" in the sidebar and then "Create Droplet."

For this guide, I'll be using Ubuntu 20.04 because that's what I'm most familiar with. You can use whatever operating system you'd prefer, but note that many of the commands used in this guide are meant for Debian-based distros and may not work on FreeBSD, Fedora, or CentOS without a few tweaks. If none of that made any sense to you, I recommend sticking with Ubuntu.

Since Ghost is quite lightweight, I recommend starting with a shared CPU like Digital Ocean's 1 vCPU / 1 GB Droplet, which starts at $5 a month.

Next select your data center location. I recommend selecting a data center that is both close to you and to your readers. For the purpose of this tutorial, I'll be using the San Francisco location.

Under authentication, select the method you'd prefer. If you have never SSHed into a machine before, don't worry, just select "Password" and enter a strong passphrase.
Here's a great comic from XKCD that explains why an easy to remember passphrase is better than a hard to remember password.

With that out of the way, click "Create Droplet." Within about a minute, your VM will have spun up.
Setting a static IP
Before you can install Ghost, you'll need need to assign a static IP — Digital Ocean calls this a Floating IP — and configure the firewall to allow traffic on the appropriate ports.


To set the IP, head back to "Droplets" in the sidebar and select your newly created VM. From the toolbar click "Enable now" next to "Floating IP." On the next page click "Assign Floating IP."
Configure the firewall

Now that you've got a static IP, you'll need to open the VM to outside traffic so your readers can access your blog. To do this, select "Networking" from the sidebar, then "Firewalls," and click "Create Firewall."


Start by giving your firewall a name. Then create two new rules for HTTP and HTTPS that allow all IPv4 and IPv6 traffic for each.
Before you continue, you'll also want to limit SSH access to your home IP for some added security. Under inbound rules, change the "SSH" entry to accept only your IP address under sources. If you don't know your IP address, you can search Google for "What's my IP?"
Finally, search for the name of your VM and click "Create Firewall" to apply it. Congratulations, you've configured a cloud firewall!
Install Docker
Next, you'll need to update the VM and install Docker. This might look a little intimidating if you've never used a Linux terminal before, but trust me, it's simpler than it looks.
To get started, access the VM's console. There are two ways to do this. The first involves using SSH. However, for this tutorial, I'll be using Digital Ocean's online console for simplicity.

You can access the web console by navigating to "Droplets" in the sidebar, selecting your VM, and clicking "Console" in the menu bar. A new window with a terminal interface will appear.
When prompted for your username, enter "root" and then enter the password you created earlier. Note: the password will not be visible when entering it.
From here, you'll need to enter a couple commands. The first two commands will update the VM, while the third will install Docker and Docker-Compose. Enter them one at a time, pressing enter after each.
apt-get update
apt-get upgrade -y
apt-get install -y docker docker-compose
If for some reason any of these commands don't work — for example, you're using a different cloud provider — you may have to type "sudo" before each to run them as the administrator.
With your VM updated and Docker ready to go, it's time to install Ghost.
Installing Ghost (The quick and easy method)
In this guide, I'll be showing you two ways to install Ghost using Docker. The first is a barebones approach which doesn't require any special configuration. Enter one line into the terminal, press enter, and presto, your Ghost blog is up and running.
This is the method I recommend for beginners, or for those using smaller VMs which don't have the resources to support multiple sites or services.
To get started, enter the following into the console and press enter — just don't forget to change "yourdomain.com" to your actual domain first.
docker run -d --name ghost-blog -e url=http://yourdomain.com -e admin.url=http://yourdomain.com -p 80:2368 ghost
After a couple seconds, your Ghost blog will be up and running. If you haven't already configured your domain's DNS settings to point to your static (floating IP), skip to Configure your DNS settings for more information.
Installing Ghost (Advanced method)
If you're looking for a bit more flexibility, or you'd like the option to host multiple websites or services on a single VM, my second "advanced" method is for you.
This method uses Docker Compose to deploy both a Ghost blog and a Nginx web server, which will serve as a reverse proxy. While there are numerous advantages to using a Nginx reverse proxy, the important one is it allows you to add additional websites or services like a self-hosted comments or an analytics server down the line. Just be warned this method is a fair bit more complex.
Creating a Docker Compose script
To get started, you'll need to create a document that tells Docker what to deploy, where to put it, and what variables to apply. Think of it like giving your order to the server at a restaurant. You tell them what you want, so the chef can make it for you.
In this case, the Docker Compose script will instruct Docker to create two containers, one for Ghost and one for Nginx.
Before you create the script, you'll want to create a new folder under the /var/ directory called "ghost," and navigate to it using the following commands.
mkdir /var/ghost
cd /var/ghost
Now that you've created the folder, you can create a docker-compose.yml file using the Nano text editor.
nano /var/ghost/docker-compose.yml
This will open the text editor to a blank document. Paste in the script below, and don't forget to replace "yourdomain.com" with your actual domain. Once complete, press Control O on your keyboard, followed by enter to save, and then Control X to exit.
version: '3.2'
services:
ghost:
image: ghost:alpine
restart: always
ports:
- 33003:2368
environment:
# this url value is just an example, and is likely wrong for your environment
url: http://yourdomain.com
admin.url: http://yourdomain.com
#Uncomment these to enable transactional emails
#mail__transport: SMTP
#mail__from: [email protected]
#mail__options__service: Gmail
#mail__options__host: smtp.gmail.com
#mail__options__port: 465
#mail__options__auth__user: [email protected]
#mail__options__auth__pass: YourAppPassword
#mail__option__secure: "true"
volumes:
- ghost-alpine:/var/lib/ghost/content
nginx:
image: nginx:latest
restart: always
volumes:
- nginx:/etc/nginx/
ports:
- 80:80
- 443:443
volumes:
ghost-alpine:
nginx:
Note: if you'd like to use Gmail for SMTP transactional emails for things like password resets, all you need to do is turn on two-factor authentication on your Google account and create an app password.
Start by removing the # symbol in front of all the "mail_" entries, and replace "[email protected]" and "YourAppPassword" with your gmail address and app password.
Launching Ghost and Nginx
Now for the easy part, starting everything up. Launch the Docker Compose script by entering the following into the terminal and press enter.
docker-compose up -d
After a few seconds, your Ghost blog and Ngnix containers should be up and running. But you're not quite finished yet.
Configuring Nginx as a reverse proxy
Before you can start blogging, you'll need to configure Nginx to send traffic from your static IP to Ghost. Thankfully, this isn't too difficult.
From the terminal, run the following command to enter your Nginx container.
docker exec -it ghost_nginx_1 /bin/bash
Note: if that doesn't work, your container may have a different name. You can find the name of your container by running:
docker ps -a
The Nginx container won't have Nano installed by default, so, before you can configure the reverse proxy, you'll need to install it using the following command. Note: If you're familiar with Vi you can skip this step and use that instead.
apt-get update && apt-get install -y nano
With Nano installed, create a Nginx configuration file by running the following command:
nano /etc/nginx/conf.d/ghost.conf
Like before, paste in the script below and don't forget to change "yourdomain.com" to your actual domain. You'll also need to replace "Your_Private_IP" with your VM's private IP, which can be found under Droplets in the Digital Ocean admin console.
server {
server_name yourdomain.com;
listen 80;
location / {
proxy_pass http://Your_Private_IP:33003;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto http;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Once complete, press Control O to save and Control X to exit.
Next, enter the following to restart Nginx and apply the configuration:
/etc/init.d/nginx restart
Configure your DNS settings
Head back to your VM in Digital Ocean and write down your your static IP (floating IP) address; you'll need this in a minute.
In order to get our domain pointed at your Ghost blog (Easy Method) or Nginx reverse proxy (Advanced Method), you'll need to configure your domain's DNS settings. I'll be showing you how to do this using Cloudflare’s free DNS. In addition to providing DNS management, the service adds a layer of protection against attacks and can significantly speed up your site. But if you’ve opted to use your domain registrar's service, the process should be almost identical.

In your DNS provider of choice, create two new A records. The first will point to your VM running in Digital Ocean, while the second will ensure that anyone typing "www." before your domain doesn't get an error page.
Type | Name | Content | TTL | Proxy |
---|---|---|---|---|
A | YOUR_DOMAIN.COM | YOUR_STATIC_IP | Auto | Proxied |
CNAME | www | YOUR_DOMAIN.COM | Auto | Proxied |
Give it a test
If you've configured your DNS records correctly, you should be able to reach your Ghost blog by typing your domain name into your browser.
Note: it may take a few minutes for the change to take effect depending on which DNS provider you're using.
Setting up Ghost

To set up your admin account in Ghost, navigate to "yourdomain.com/ghost". If you've configured SMTP for transactional emails, you'll be given the opportunity to invite staff members.
Securing your blog
We're almost done, I promise. Before you start your first post, you'll want to lock down your site.
You might have noticed the little message in your address bar warning you that your blog is "Not Secure." That's because you need an SSL certificate.
The easiest way to solve this is to use Cloudflare's edge certificate. It's not perfect, but for most blogs, where you're not collecting sensitive information from your readers, it's good enough. More importantly, it's really easy to set up.

Start by heading back to Cloudflare. Under the "SSL/TLS" tab, change your encryption mode to "Flexible." This will ensure all traffic from the browser to Cloudflare is encrypted.

Next, you'll want to make sure anyone who types HTTP instead of HTTPS into their browser is redirected to the secure connection. To do this, head over to the "Edge Certificates" tab under SSL/TLS and turn on "Always Use HTTPS."
Let's Encrypt with Certbot (Advanced method only)
If you're not using Cloudflare, or you'd prefer to have end-to-end encryption, you can use Certbot to issue a Let'sEncrypt SSL certificate. You can learn more about configuring Certbot here.
To get started, head back into the Nginx container by running:
docker exec -it ghost_nginx_1 /bin/bash
Next, install and run Certbot with the following commands:
apt-get update && apt-get install certbot python-certbot-nginx
certbot --nginx
Follow the instructions on screen. When asked whether you would like Certbot to redirect traffic from HTTP to HTTPS, select the option to do so (usually option 2).
Reconfigure Nginx for SSL (HTTPS)
There is a good chance that, while updating your blog's config file, Certbot may have broken something. If this happens to you, open your ghost.conf file using Nano.
nano /etc/nginx/conf.d/ghost.conf
Next, change the "proxy_set_header X-Forwarded-Proto" entry so it ends in https instead of http.
proxy_set_header X-Forwarded-Proto https;
Once everything looks right, press Control O to save and Control X to exit. Finally, restart Nginx to apply the configuration by running:
/etc/init.d/nginx restart
Note: if you are still having trouble connecting to your site, make sure Cloudflare's SSL has been changed to "Full" or "Full (strict)."
Clean up Docker Compose
With the reverse proxy set to use a Let'sEncrypt SSL certificate, you'll need to update your Docker Compose script to reflect that. To do this, open the script in Nano.
nano /var/ghost/docker-compose.yml
Next, update the "url" and "admin.url" entries with "https".
url: https://yourdomain.com
admin.url: https://yourdomain.com
Once complete, press Control O and enter to save, and Control X to exit.
Finally, rerun the Docker Compose script to apply the changes.
docker-compose up -d
Helpful resources if you run into trouble
I covered a lot in this tutorial, so please let me know if you got stuck at any step along the way. I'm happy to answer your questions and update this post with additional details.
If you need help with something not covered in this post, I recommend you check out these excellent resources:
You did it!
Congratulations! You've successfully launched a Ghost blog in Digital Ocean. Now it's time to celebrate by writing your first blog post.
If you found this guide useful, please take a moment to leave a comment below. I'd love to hear how your experience went, and if you ran into any trouble along the way.
More to come
This post is part of a larger series on blogging that will be released over the next few months. Find all the posts in the series at the link below.
What do you think? Share your thoughts in the comments below:
- Where do you host your blog?
- Do you prefer the simplicity of managed hosting or the flexibility of the cloud?
- Would you like to see guides for other cloud providers like Microsoft Azure or Amazon Web Services?
- How about other platforms like Joomla or Drupal?
If you liked this post, please consider becoming a member. You'll get access to all of my posts weeks before they go public and get access to the comments section. Just enter your email in the form below, it's that simple.