Instant Django Dev Environments with Tmux, Tmuxinator, and Virtualenvwrapper

This post describes how to use a few common tools to instantly set up and tear down Django development environments.

I’ve found that such automation is most useful when switching between branches in source control. Without automation, you have to manually kill and reconstruct any Django shell and development server instances for each branch (and sometimes the database shell, too).

While my examples are geared toward Django development, the approach I describe works equally well with Rails and other frameworks.

Tools used:

  • Tmux, a terminal multiplexer like GNU Screen
  • Tmuxinator, a nice Ruby gem for automating Tmux
  • Virtualenv, a tool for creating isolated Python environments
  • Virtualenvwrapper, a set of extensions to virtualenv
  • My tmux.conf

By the end of this post, you will have a new command available in your terminal that will create a Tmux session with multiple views into your Django project.

Why Tmux?

First, a quick note about Tmux.

Until earlier this year, I had used GNU Screen for managing persistent terminal sessions. It worked well. However, I grew dissatisfied with the pace of development in the project, the lack of new features, and the inconsistent availability of vertical splits across different platforms. So, I tried Tmux, and I haven’t gone back.

If you are a Screen user, then I suggest you try Tmux, but you can probably achieve a similar method to the one described in this post using Screen and one of the automation scripts designed to work with it.

Virtualenv and Virtualenvwrapper

My example uses virtualenv and virtualenvwrapper. Note that you do not need to use either of those tools to set up or tear down a Django development environment with Tmux. They just make working with multiple Python projects easier.

If you don’t use these tools, remove the venvwrapper && workon my_project portion of the examples.

I will skip explaining how to install those tools or use them, since many tutorials on those subjects exist already, in addition to the virtualenv docs and virtualenvwrapper docs.

After this point I will assume you have created a virtualenv for your Python code in a directory named my_project and have configured virtualenvwrapper so that you can execute workon my_project to activate the virtualenv.

Install Tmux and Tmuxinator

Install Tmux with your package management software of choice. It’s available in apt-get, MacPorts, etc.

Next, install Tmuxinator (instructions here).

As a side note, I recommend using RVM to manage your Ruby versions.

Create a Project File

Before taking this step, make sure you’ve followed the Tmuxinator installation instructions and have exported your EDITOR shell variable, e.g.:

$ export EDITOR=vim

Now, use Tmuxinator to create a “project file” (a YAML file containing shorthand configuration settings for Tmux).

$ tmuxinator new my_project

A file should now open in your text editor. The Tmuxinator docs use a Rails-inspired example. The following is a Django example in which I create a Tmux session with multiple windows (an explanation follows the example).

A Django Example

# ~/.tmuxinator/my_project.yml
# you can make as many tabs as you wish...

project_name: my_project
project_root: ~/
tabs:
  - zsh: ls -l
  - shell: venvwrapper && workon my_project && cd my_project &&  ./manage.py shell
  - database: venvwrapper && workon my_project && cd my_project && ./manage.py dbshell
  - server: venvwrapper && workon my_project && cd my_project && ./manage.py runserver
  - logs:
      layout: even-horizonal
      panes:
        - tail -f /opt/local/var/log/nginx/access.log
        - tail -f /opt/local/var/log/nginx/error.log
        - sudo python -m smtpd -n -c DebuggingServer localhost:25

Explanation of the Example

The example creates a Tmuxinator project file named my_project.yml, which generates a my_project.tmux file and creates the command start_my_project that you can use to start the Tmux session.

The example assumes that a virtualenv exists in a directory named my_project and the env directory contains a directory named my_project (e.g., in my case, ~/envs/my_project/my_project). Obviously, this should match whatever directory structure you actually use for your virtualenvs.

The Tmuxinator docs describe everything that you may configure with a project file. There are more options available than the ones in my example, but I use this relatively simple configuration for my day-to-day Django work, and it saves a bunch of time.

Here is an explanation of the options the example uses:

  • project_name: the name of the project; this becomes part of the command you use to launch a Tmux session, e.g., start_my_project
  • project_root: the starting directory for each command that you tell Tmuxinator to execute
  • tabs: each item in this list represents a named “tab” or Tmux window in which one or more shell sessions will run
  • zsh: I like to have a shell opened and pointed at my home directory for random commands
  • shell: activate the virtualenv and load the Django shell for my project
  • database: activate the virtualenv and load the database shell for my project
  • server: activate the virtualenv and start the Django development server for my project
  • logs: create a three-pane split using Tmux’s even-horizontal layout (each pane is an even height) to display logging output from nginx, which is running my dev machine and proxying requests to the Django dev server, and a Python SMTP debugging server (because I like to see HTML/text dumps of emails generated during development)

Now, assuming you followed the Tmuxinator docs and have the following line in your zshrc/bashrc (…):

# Tmuxinator
[[ -s $HOME/.tmuxinator/scripts/tmuxinator ]] && source $HOME/.tmuxinator/scripts/tmuxinator

… You can run start_my_project from your shell of choice to open a Tmux session with all the tabs you configured.

Making this Really Useful

Instantly summoning a development environment is great, but where this approach really shines is when you need to switch branches in your project.

In a single day, I might work on two or three different branches in one project. Before using Tmux and Tmuxinator, I would have to constantly kill and restart various commands each time I switched to a different branch. Now I just do the following:

  • Issue the kill-session command within a loaded Tmux session (Ctrl-b : kill-session <CR>)
  • Back at the shell, run my start_project command again

Two commands to restart an entire session from the new branch. Handy! And, if you have other sessions loaded, kill-session preserves them by killing only the session you are currently in (kill-server kills all sessions).

Summary, Other Resources

Tmux is great, and automating session creation has saved me a lot of time. I hope it saves you a few precious minutes (hours?!), too.

If you’re a Screen user who has read this far, I encourage you to try out a similar approach with Screeninator. Of, if you just want to get some tabs up in your terminal app, try Terminitor.

Addendum: Multiple Sessions

One thing I love about Tmux is that you can have multiple sessions open. So, for example, while I’m working I usually have two sessions open: my Django project session and a session for system administration that has my Chef repository (using LittleChef) and a few SSH tabs open:

# ~/.tmuxinator/work.yml
# you can make as many tabs as you wish...

project_name: work
project_root: ~/
tabs:
  - zsh: ls -l
  - chef: venvwrapper && workon kitchen
  - qa: ssh username@my_qa_server
  - prod: ssh username@my_prod_web_server
  - (several more ssh sessions here)

This is nice because I can quickly switch sessions within Tmux with Ctrl-b s.

I also have other sessions for Clojure projects and so forth that I’ve configured for hacking around on the weekend. Go Tmux!