Better application deployment with DigitalOcean, Terraform, Ansible and Docker. Creating basic terraform instances


It’s not a rare examples, when people need to deploy their applications somewhere in the cloud, without having mature infrastructure, and just for testing purpose. Let’s say, you’re writing a backend part for mobile application, and your android developer needs to have backend right and now. Your actions ? Of course, create EC2/Droplet instance, scp all binaries, connect via ssh, run them. You can even write some bash script, which will do all the work. And what should you do, if some day you will decide to go live ? The same thing, you will order some instances in your favorite cloud, configure load balancer, add 1-5 instances and start running your application. But how to scale ? What, if you will decide to add couple servers, and balance them ? By the time you will need to do that, you will be the author of many scripts, which potentially have bugs. You should have something better in your arsenal. I spend couple of weeks to find an instrument, which will fit my needs, and want to share it here, in my blog. It’s not claims to be the best solution, I’m sure, there’s plenty of them, but by following series I’ll explain what I’ve achieved.

Whom this article is for ?

This is for software engineers, who wants to improve their devops processes. I don’t expect, that true devops will find something useful here, because they should already have a good bundle of instruments.

General idea

You will use three projects:


We will use Terraform to create instances in the cloud. I will use digitalocean.com, it’s less mature cloud provider, than amazon.com, but I love it, and want to deploy my apps there. Feel free to try EC2 for yourself.


Here’re the steps you need to do before creating cloud instances:

Create template file

Now, as you did all the required steps, you can start configuring your bootstrap script. Enter your folder, and create another folder, called dev. Why dev ? Actually, you should be ready to have different environments: one for dev, another for staging, and, finally, prod. Furthermore, according to ansible best practices, my suggestion is one of the options, that ansible recommends. Ok then, enter dev folder and create a file called variables.tf

variable "token" {
  description = "Digital Ocean Access Token"
  default = "your digital ocean token"

variable "ssh_fingerprint" {
  description = "ssh fingerprint"
  default="Your ssh fingerprint. Get it via command line, or get it here - https://cloud.digitalocean.com/settings/security"

variable "pub_key" {
	description = "ssh public key"
	default = "Your ssh public key. Copy it as it is."

variable "pvt_key" {
  description = "ssh private key"
  default = "Your private key. Copy it as it is."

variable "region" {
  description = "Digital Ocean region"
  default = "ams2"

Later on, you can remove default values, and pass everything from command line, but for now, let’s keep it simple, to avoid any pitfalls.

Create your main terraform file

Now, here’s the final step in configuring terraform - specifying what machines do you want. Lets name it as main.tf:

provider "digitalocean" {
  token = "${var.token}"

resource "digitalocean_droplet" "app-rest-api" {
  count = 1
  image = "ubuntu-14-04-x64"
  name = "app-rest-api"
  region = "${var.region}"
  size = "1gb"
  ssh_keys = ["${var.ssh_fingerprint}"]

You should have following folder structure:

└── dev
    ├── main.tf
    └── variables.tf

1 directory, 2 files

Then, enter dev folder, and run

terraform plan

This command will plan how many instances should be created, and will output it. I will show some tricks later about your instances modification. You should get similar output:

Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but
will not be persisted to local or remote state storage.

The Terraform execution plan has been generated and is shown below.
Resources are shown in alphabetical order for quick scanning. Green resources
will be created (or destroyed and then created if an existing resource
exists), yellow resources are being changed in-place, and red resources
will be destroyed. Cyan entries are data sources to be read.

Note: You didn't specify an "-out" parameter to save this plan, so when
"apply" is called, Terraform can't guarantee this is what will execute.

+ digitalocean_droplet.app-rest-api
    image:                "ubuntu-14-04-x64"
    ipv4_address:         "<computed>"
    ipv4_address_private: "<computed>"
    ipv6_address:         "<computed>"
    ipv6_address_private: "<computed>"
    locked:               "<computed>"
    name:                 "app-rest-api"
    region:               "ams2"
    size:                 "1gb"
    ssh_keys.#:           "1"
    ssh_keys.0:           "${ssh_fingerprint}"
    status:               "<computed>"

Plan: 1 to add, 0 to change, 0 to destroy.

If you’re okay with this configuration, you can start creating instances.

terraform apply
digitalocean_droplet.app-rest-api: Creating...
  image:                "" => "ubuntu-14-04-x64"
  ipv4_address:         "" => "<computed>"
  ipv4_address_private: "" => "<computed>"
  ipv6_address:         "" => "<computed>"
  ipv6_address_private: "" => "<computed>"
  locked:               "" => "<computed>"
  name:                 "" => "app-rest-api"
  region:               "" => "ams2"
  size:                 "" => "1gb"
  ssh_keys.#:           "" => "1"
  ssh_keys.0:           "" => "${ssh_fingerprint}"
  status:               "" => "<computed>"
digitalocean_droplet.app-rest-api: Still creating... (10s elapsed)
digitalocean_droplet.app-rest-api: Still creating... (20s elapsed)
digitalocean_droplet.app-rest-api: Still creating... (30s elapsed)
digitalocean_droplet.app-rest-api: Creation complete

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: terraform.tfstate

Now it’s ready! You can check your digitalocean instances page and make sure there’s some instances with app-rest-api name.

You should notice another file in your dev folder, terraform.tfstate:

├── main.tf
├── terraform.tfstate
└── variables.tf

0 directories, 3 files

This file contains all the necessary information about your instances. Feel free to cat terraform.tfstate it, and see what it contains.

Now, change your instance from 1gb to 2gb:

size = "2gb"

And run terraform plan again:


~ digitalocean_droplet.app-rest-api
    size: "1gb" => "2gb"

Plan: 0 to add, 1 to change, 0 to destroy.

You see it ? We increased RAM of our machine, and terraform found out, that we need to change this machine.

Run terraform apply again and make sure your digitalocean droplet is resized.

This is the first chapter, which explains how to work with terraform. In the next part I will show how to set dns records for your instances

PS - this blog post is now a part of Cloud Application Deployment WIKI