Managing Development Environments with Tmux and Tmuxinator
Like many developers, I work on quite a few different code bases between my job and my side projects. Often I need to be able to quickly start and switch between projects. A modern web development workflow has quite a few moving parts and might require a combination of:
- Development server such a Vagrant, Docker, or perhaps simply
- Task runner like Webpack or Gulp
- Log viewer
- Text editor
- Database Console
Not every project requires the same set of tools either. My static website, for example, requires the Jigsaw server, Webpack, a text editor, and a shell. There is no database or log files. Other projects might be using Docker or Homestead, Gulp instead of Webpack. Some Postgres, others MySQL.
Personally I like a lot of terminal or CLI (command line interface) programs. In fact, the only desktop apps I often have open are a terminal and a web browser.
What is Tmux and How Does it Help?
Tmux is a "terminal multiplexer" which basically means that it allows you run multiple programs within a single terminal and switch between them all. These programs can be divided into different sessions, windows (tabs), and panes. You can "detach" from tmux and it will keep everything running in the background for you to reattach to later (perhaps from a different terminal!).
Switching between sessions shows you a preview of each window in the session.
I like to start a separate tmux session for each project that I work on. Within that session I will typically have one window (tab) for each process. I don't often divide windows into panes but there are times when it can be useful.
Starting a session and opening windows in tmux is easy, but it's a pain remembering which specific processes I need to start for each project, especially for those I only work on sporadically. I don't want to have to think about that or start anything manually, especially when I'm ready to dive right into the code. I just want to think about the project I want to work on, type a quick command, and have everything ready to go.
Tmuxinator to the Rescue!
Tmuxinator bills itself by saying "Create and manage tmux sessions easily". It allows me to type that quick command and have everything ready to go.
After installing Tmuxinator, you can define a project using:
$ tmuxinator new [project]
This will create a YAML configuration file for the project with comments describing the available options.
An example for a project I've been working on lately looks like this:
# ~/.config/tmuxinator/lister.ymlname: listerroot: ~/Code/listeron_project_start: homestead up windows: - server: homestead ssh - logs: tail -f storage/logs/*.log - runner: npm run watch - vim: vim - mysql: homestead ssh -c mysql - shell:
I hope the options are self-explanatory. The key for me is the
windows array where I can define all of the windows and processes I require.
Now whenever I want to start working on that project, I can simply type (with the help of an alias):
$ mux start lister
If the project isn't already running, it will first make sure Homestead is up, and then it will start a new tmux session with my chosen name and the windows and processes I have configured. I can close my terminal window at any time and it will automatically detach. If I run the above command and the project is already running, it will simply attach to the existing session.
If you like splitting your windows into panes, tmuxinator comes with a decent set of preset layouts you might like to explore. Alternatively, you can configure your own custom window layout.
It is also possible to define a project as a template and start multiple tmux sessions from that one definition. For example, you might define a tmuxinator project called "laravel" and use that one definition for any Laravel project you work on. In practice I've found that most of the projects I work on have too many nuances for this to be useful though.
Hopefully this gives you a taste of a modern terminal-based workflow. If you're interested in exploring this further, the tmuxinator README is comprehensive. My own config files are available in my dotfiles repo, with the exception of my tmuxinator project definitions. I keep these in a privately hosted repository because they disclose some internals about projects that I'm not permitted to disclose.