How to Provision Vagrant Boxes with Ansible

In this tutorial, we’re going to show you how to use your Ansible playbooks to provision your Vagrant boxes. Configuration management is key to ensuring your test and development servers match your production servers. Without which predicting the behavior of your application after deployment is impossible, since every environment might be set up slightly different.

We’re going to create a multi-server Vagrantfile for this tutorial. The first server will be our NodeJS web application and the second will be the database server.

Two different playbooks will be used — one for each server role. Each playbook will have multiple Ansible roles for the different components that will be configured for each server.

Workspace Setup

Create the following directory structure for your Vagrant workspace. The directory will host the Vagrantfile, as well as our Ansible playbooks and roles.

/Vagrant
    Vagrantfile
  / Provisioning
    all.yml
    database.yml
    webapp.yml
    / roles
      / common
        / tasks
          main.yml
      / webapp
        / tasks
          main.yml
      / mysql
        / handlers
          main.yml
        / tasks
          main.yml
        / templates
          mysql.conf.j2

All Playbook

The all.yml playbook will apply all servers roles to the targeted host. It will deploy and configure our web application and install the database server. This is useful for when we want to consolidate all roles onto a single server.

Webapp Playbook

The webapp playbook will configure the server to host our web application. It will also deploy the application to the server after it has been configured.

Database playbook

The database playbook will install and configure our backend database server, MySQL.

Vagrant Ansible Provisioner

When your Ansible playbooks and roles are ready you will need to tell Vagrant to use them during the provisioning stage.

Single Server Vagrantfile

In our single server example, we’re going to apply all server roles to our instance using the all.yml playbook. This is the simplest, lightest method of creating a development box on your desktop or laptop.

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.box_check_update = true

  config.vm.hostname = "myserver01"
  config.vm.network "private_network", ip: "192.168.50.50"

  config.vm.provider "virtualbox" do |v|
    v.name = "myserver01"
    v.memory = 1024
    v.cpus = 1
    v.linked_clone = true
    v.gui = false
  end

  server.vm.provision "ansible" do |ansible|
    ansible.playbook = "provisioning/all.yml"
  end
endMulti Server Vagrantfile

Multi-Server Vagrantfile

To better mimic your staging or production environments, you may want to separate everything onto different hosts. The following vagrant file will create three instances, two for hosting our web application and the third that will host our application’s database.

The two webapp servers will use our webapp.yml playbook. The database server will use the database.yml playbook.

Vagrant.configure("2") do |config|
    config.vm.box = "ubuntu/xenial64"
    config.vm.box_check_update = true

    config.vm.define "webapp1" do |server|
        server.vm.hostname = "webapp1"
        server.vm.network "private_network", ip: "192.168.50.30"
        server.vm.provider "virtualbox" do |v|
            v.name = "webapp1"
            v.memory = 768
            v.cpus = 1
            v.linked_clone = true
            v.gui = false
        end
        server.vm.provision "ansible" do |ansible|
            ansible.playbook = "provisioning/webapp.yml"
        end
    end

    config.vm.define "webapp2" do |server|
        server.vm.hostname = "webapp2"
        server.vm.network "private_network", ip: "192.168.50.31"
        server.vm.provider "virtualbox" do |v|
            v.name = "webapp2"
            v.memory = 768
            v.cpus = 1
            v.linked_clone = true
            v.gui = false
        end
        server.vm.provision "ansible" do |ansible|
            ansible.playbook = "provisioning/webapp.yml"
        end
    end

    config.vm.define "db1" do |server|
        server.vm.hostname = "db1"
        server.vm.network "private_network", ip: "192.168.50.32"
        server.vm.provider "virtualbox" do |v|
            v.name = "db1"
            v.memory = 1024
            v.cpus = 1
            v.linked_clone = true
            v.gui = false
        end
        server.vm.provision "ansible" do |ansible|
            ansible.playbook = "provisioning/mysql-server.yml"
        end
    end
end