Your Vagrant Deployment Sandbox for Django

Deploying your Django project for the first time can seem like a huge task, and a blocking one in addition to that. You can’t move forward until the site is in production. The pressure is on.

However, there’s one trick to make deployment way less scary, and way less of a big deal.

Just deploy as early as possible. It doesn’t even need to be a publicly accessible location. Just an environment which is distinct from your development environment. Somewhere, where you can try your deployment procedures from scratch every now and then.

If this sounds painful and tedious, this article is for you. Deployment doesn’t need to be a frustrating and task which costs you days to get right.

A Safe Environment

There’s the perfect tool, to create safe environments without much effort - it’s Vagrant.

“Vagrant enables users to create and configure lightweight, reproducible, and portable development environments.”

Vagrant makes it possible to create a virtual machine with a single command, configure it with code, and completely clean up the environment (and recreate it from scratch) whenever you wish.

It makes for the perfect environment to prototype your deployment approaches. A completely safe sandbox to try to deploy your application and find out what’s missing and what can be done better.

Your Goal

The idea behind deploying early, and doing so in an environment which is meant to be destroyed eventually, is to work on your processes. You want to make deployment really easy, almost boring.

This is done by iterating and learning as you go. In the beginning, you’ll be working on making notes for your future-self. Jotting down notes and tweaking your webapp project to make it more deploy-friendly. Eventually, you’ll start adding automation to your written documentation, to make each time you recreate your deployment environment easier and less intimidating.

If anything goes wrong or if you don’t know how to fix an issue - no biggie. You can simply completely destroy the VM, restart from scratch and arrive at a working state again.

Vagrant for your Django App - Step by Step

Here’s how you can create your first, safe Django deployment environment - right on your development machine. You’ll be able to get your feet wet here without the perceived responsibility and steep learning curve of working with a publicly-accessible remote environment.

1. Install Vagrant.

To get started, you’ll need to install Vagrant on your local development machine. Go to Vagrant’s download page and install the most recent version from there.

“Why not use the package manager version” you may ask? You could, but for example the Ubuntu one is known to be notoriously outdated. This sometimes leads to bugs and unexpected behaviour. I find it easier to roll with the versions from the site for this reason.

Once you’ve installed it, you can jump into your command line and check the version:

$ vagrant --version

2. Install VirtualBox

Vagrant is a tool to create and manage environments which run in in VMs. VirtualBox is a great free virtualization product. The way how you’re supposed to install it depends on your OS. Make sure to set it up well.

3. Get a Vagrant Box

Vagrant boxes are basically VM images, which you use to create a new virtual machine. I like to use Ubuntu, as it is easy to handle, and offers mix of stable & reasonably recent packages. The current LTS (long time support) version is Ubuntu 18.04.2 LTS.

With Vagrant, you can get Ubuntu boxes from different providers.

Check out the search page for more OS choices.

The Vagrant docs recommend to stick to Ubuntu boxes from either bento or hashicorp themselves. (Read here for more details). The boxes from the canonical namespace are not recommended as they are known to cause issues.

“These are the only two officially-recommended box sets.”

While Hashicorp’s box is described as “minimal, highly optimized, small in size”, while the bento box is recommended “for other users”. From the box site, both seem to be around 500 MB large. I went with the bento version out of habit - they haven’t had any issues with them in the past.

You can add a box (downloading it to your machine) with the following command:

$ vagrant box add bento/ubuntu-18.04

# Note: choose option 1 for virtualbox

4. Start a Vagrantfile

Now you have everything ready to create a Vagrantfile.

Go into the folder where your Django app is located, and execute the following command:

$ vagrant init bento/ubuntu-18.04

# you could also specify a particular version:
# --box-version 201906.18.0

This will create a well-documented Vagrantfile for you, which you can build on. You’ll probably only need to uncomment parts and slightly tweak some of them.

Here is my (slightly modified) file:

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://vagrantcloud.com/search.
  config.vm.box = "bento/ubuntu-18.04"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # NOTE: This will enable public access to the opened port
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine and only allow access
  # via to disable public access
  config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: ""
  config.vm.network "forwarded_port", guest: 8000, host: 8081, host_ip: ""

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: ""

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  config.vm.synced_folder ".", "/home/vagrant/code"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
   config.vm.provider "virtualbox" do |vb|
     # Don't display the VirtualBox GUI when booting the machine
     vb.gui = false

     # Customize the amount of memory on the VM:
     vb.memory = "1024"

  # View the documentation for the provider you are using for more
  # information on available options.

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   apt-get update
  #   apt-get install -y apache2

The Vagrantfile is modified to:

  • Add a shared folder (the one where the Vagrantfile is in) to the VM under /home/vagrant/code.
  • Enable “headless mode” so no window pops up where the VM is running.
  • Set a memory limit for the VM to 1 GB.
  • Forward port 80 of the VM to and port 8000 to on the host.

Now we are ready to start a brand-new Ubuntu VM based on those configs.

5. Vagrant Up!

With all of the preparations in place, creating a new VM is as easy as typing:

$ vagrant up

Once it has started, and you make changes to your Vagrantfile, you’ll need to run

$ vagrant reload

for the changes to take effect.

Now you can SSH into your box.

6. SSH Into Your VM

Once your Vagrant VM is up, you can connect to it directly via the commandline:

$ vagrant ssh

Congratulations! You’re in your brand-new Vagrant environment.

Your project’s code (shared from the local machine) can be found in the directories

/home/vagrant/code or /vagrant in the new VM. Check out the content:

$ cd code
$ ls

As it’s a shared folder, you should be aware that any file you delete or change here, will be deleted or changed on your development machine as well. Take care, and make sure to have your code under version control, so you could recover from things going wrong.

The upside of a shared folder is: you can edit code as usually on your machine, and your VM will get the newest changes right away.

7. The First “Deployment”

Now that we have made our way into a brand-new environment, we can get a very first deployment version of your application up and running.

In the beginning, it’s best to aim for the easiest possible option - simply bring up a development server inside of the VM. Once that one works, you’ll be able to make it more complicated. Consider it a “Hello World!” of deployment.

Be sure to take notes of all steps which you are taking. Jotting down info on the packages you need to install or commands you type in this environment are useful clues to make it easier the next time around! Don’t forget, you’re working on your deployment processes, not building a “forever” deployment environment. Being conscious of those processes is the first step forward.

8. An Example

As I don’t want to leave you hanging 1:1 with this (possibly) new environment, I’d like to share a series of commands which will help you to get up a brand-new Django project inside of the VM for testing.

Attention! If you already have a Django project in your VM’s code directory, you won’t need to create a new one. Everything else should be pretty much the same.

First, we’ll need to install Python 3, and add some tooling while we’re at it:

$ sudo install python3
$ sudo install python3-pip
$ pip3 install virtualenv
$ pip3 install pipenv

Now, we should exit the SSH session, and start a new one with vagrant ssh. This will make sure that we can use pipenv.

Here, we’ll create a new Pipfile and install the latest version of Django. These steps are loosely based no this article. Then, we’ll use that Django version to start a new project and run it inside of the VM:

$ pipenv --three
$ pipenv install django

$ pipenv shell
$ django-admin startproject testproject .

$ python manage.py runserver

Notice how we run the development server on inside the VM? Because of the port forwarding settings in the Vagrantfile, we can access this running application from outside of the VM. Just open up your browser, and navigate to

All done! We have created a new Django VM, installed everything we need to run a Django project in it, and accessed the running application from the development machine. Shiny!

8. Total Destruction

Once you feel that you are ready to try a more automated, or more elaborate way to deploy your application, you can get rid of the complete Vagrant VM, and start from scratch. This is a good thing! You’ll have a fresh OS, which is in a blank state. This will help you to make sure that your processes are complete and reproducible.

Destroying the environment and starting from scratch is as easy as typing the following two commands:

$ vagrant destroy
$ vagrant up

Now, Vagrant will create a fresh VM. I hope your notes from the last time around are good, and you’ll get your environment up even faster!

Cool. Now What?

This was only the first non-development (but still develop-ish) environment you’ve created. Congratulations! You are now ready to build on your hard work, and bring it closer to a production environment.

Don’t forget: your goal is to build reproducible processes, not to “get it working once and forget how”. These processes will help you to shape your application to be easy to deploy, but they’ll also get you in the habit of bringing your project into a deployed state.

Here is what you can try next:

  • Install a reverse proxy like Nginx, and make it pass traffic to your app.
  • Install a PostgreSQL database inside the VM, make your app use it.
  • Use Gunicorn instead of the development server to run your code.
  • Automate some setups steps of the Vagrant VM - using either Bash, Ansible or Fabric.
  • Try Docker for some of your backing services.

I hope you’ll be able to learn about deployment way earlier than the dreaded “time to deploy”, and get comfortable with the task in a safe, fully controlled environment. If it seems like repeating yourself - don’t worry. Approaching deployment iteratively will make it less stressful and save you time down the line.

Wanna learn how to use Vagrant better, and develop your first deployment environment into a production-like environment for your Django app? Sign up below and get the first lesson directly in your inbox!