Setting up a blog with Ghost and Digital Ocean


DigitalOcean makes it extremely simple to set up a Ghost-powered blog. A couple of clicks and you get a droplet with Ghost pre-installed and running, all without having to write a single line of code.

But, while DigitalOcean does a good job of getting something running, there still is some work to do. In fact, there are enough steps that DigitalOcean even compiled a tutorial explaining the process. Go ahead and take a peek at "Step 2: Setting up a domain name" which is the part we're going to be talking about today.

As you can see, once the server is running, both NGINX and Ghost need to have their config files modified in order to complete the set up. None of these steps are difficult but they represent a manual process, which means they don't have the same degree of visibility and transparency that code changes do.

Manual work is also expensive to repeat, prone to human error, and leads to special "snowflake" servers. Anything we can do to move this work under version control and, ideally, automate it will save us time and frustration in the future.


  1. Track all Ghost and NGINX configuration changes in version control.
  2. Automate server provisioning.


  • Git
  • Ansible

Step 1: Create a repo

Open up a new terminal window and create our project.

mkdir ghost-blog  
cd ghost-blog  
git init  
touch roles.yml hosts playbook.yml  

Add the following to roles.yml:

- src:

Now install the role using the ansible-galaxy command.

➜  ghost-blog git:(master) ✗ sudo ansible-galaxy install -r roles.yml
[sudo] password for jvalentini: 
- downloading role 'blog-ghost', owned by jvalentini
- downloading role from
- extracting to /etc/ansible/roles/
- was installed successfully

Next we need to set up the inventory file against which Ansible will run the playbook. Open up the hosts file we created earlier and add:


Replace with the host name of your site.

Finally, it's time to create playbook.yml and use the new role and inventory group.

- name: provision ghost
  hosts: blog
  remote_user: root
    mail_pass: <secret password>

If you provide the mail_user and mail_pass vars the playbook will setup Mailgun integration for you. This is useful for sending forgot password emails in addition to other things.

Now we can execute our playbook with the following:
ansible-playbook -i hosts playbook.yml

➜  ghost-blog git:(master) ✗ ansible-playbook -i hosts playbook.yml

PLAY [provision digital ocean ghost application] *******************************

TASK [setup] *******************************************************************  
ok: []

TASK [../ansible-role-blog-ghost : updating nginx server_name] *****************  
ok: []

TASK [../ansible-role-blog-ghost : updating ghost url] *************************  
changed: []

TASK [../ansible-role-blog-ghost : updating ghost mail] ************************  
changed: []

RUNNING HANDLER [../ansible-role-blog-ghost : restart ghost] *******************  
changed: []

PLAY RECAP *********************************************************************          : ok=5    changed=3    unreachable=0    failed=0