Been using Vagrant for a while now for setting up my development environments. Vagrant is a great tool that automates setting up virtual machines using virtual box has a hypervisor and even setting up remote Virtual Machines (VMs) in public clouds such as AWS, Linode, Azure etc. With Ansible, the configuration management tool, you can automate manual installation of your development and even production environments with a few commands. For instance, you can setup the famous LAMP stack quite easily and quickly using a combo of Vagrant and Ansible. Vagrant is developed by folks at HashiCorp.

Recently or actually starting this year, I have increasingly been relying on Docker for building and deploying Apps. Even for file system heavy Apps like Wordpress sites, I am experimenting with Docker containers. My side project Site Monki is all completely dockerized and the experience hasn't been that bad.

Once I figured out how to use Docker volumes and services, I am quite confidant now to use Docker containers not just for production, but even my development workflows. On my development machine, I use unnamed docker volumes which map host folder with app code with a folder inside the Docker container. This means I don't have to keep rebuilding the docker containers every time I make changes to the app code. I find rebuilding docker containers all the time even with minor code changes quite annoying and time consuming, so this is why I didn't use them much in development. Instead, I would use a Vagrant VM with a shared folder that syncs codes between host OS and the VM.

My sample Vagrantfile which is used to create VMs

username="dave"
Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.define username
  config.vm.network "private_network", ip: "192.168.10.195"
  config.vm.network "forwarded_port", guest: 80, host: 8090
  config.vm.network "forwarded_port", guest: 8000, host: 9090
  config.vm.provision "shell", inline: "apt-get update && apt-get -y install python"

  config.vm.provider :virtualbox do |virtualbox|
    # allocate 2048 mb RAM
    virtualbox.customize ["modifyvm", :id, "--memory", "2048"] 
    # allocate max 50% CPU
    virtualbox.customize ["modifyvm", :id, "--cpuexecutioncap", "50"]
  end
  config.vm.provision "shell", inline: <<-EOF
                                        id -u #{username} &>/dev/null || useradd -md /home/#{username} -s /bin/bash #{username} \
                                        && cp -pr /home/vagrant/.ssh /home/#{username}/ \
                                        && chown -R #{username}: /home/#{username} \
                                        && echo "#{username} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/#{username}
                                        EOF
  config.vm.synced_folder "vm_apps/", "/home/#{username}/vm_apps", type: "virtualbox",
                                                                 owner: username, group: username
  config.vm.provision "ansible" do |ansible|
    ansible.verbose = "v"
    ansible.playbook = "docker.yml"
    #ansible.playbook = "ansible-playbooks/provision.yaml"
  end
  VAGRANT_COMMAND = ARGV[0]
  if VAGRANT_COMMAND == "ssh"
    config.ssh.username = username
  end
end
```

The issue with Vagrant is that it has a bigger overhead than the more lightweight Docker containers. Given I am running on a local machine with Core i5 8th Gen CPU and just 8GB of RAM, my machine just couldn't keep up with a whole VM running in the background. Docker containers are super lightweight because they don't visualize and emulate a whole OS including the kernel the way that VMs do.

I am using docker-compose tool and the docker-compose.yml file where I define all my containers or services and how they interact with each other. I keep a separate docker-compose-dev.yml file which is tweaked for my local development requirements. For instance in production I use named volumes, while in development I use unamed volumes synced between host folder and container folder so I don't have to keep rebuilding containers every time I make changes to the code.

Vagrant+Ansible combo is still great of course. You still have to setup and configure the Host OS which runs your docker containers. For instance, you are most likely going to install Nginx reverse proxy and SSL certificate on the host machine which proxies requests to docker containers. You can do this by hand or use Vagrant and Ansible to automate host machine setup. If you have a huge setup then you even want to consider using Terraform instead of Vagrant for automating setting up infrastructure for your App.