In today’s Rails quick tip we’ll look at my local development workflow, specifically tmux, Overmind, and tmuxinator.
tmux is what’s called a “terminal multiplexer”. The documentation explains what exactly that means as follows:
It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal.
The following screenshot shows a single tmux window with 3 panes: one displaying a greeting via figlet, one running
hugo serve and a Neovim session for editing this blog post:
While this is already pretty cool, tmux really shines in combination with iTerm’s tmux integration.
The two highlighted settings ensure that
- each tmux window becomes a separate tab in the attaching window and
- that the attaching tab gets “buried” (hidden) until the end of the session.
Overmind is a process manager for
Procfile-based applications that utilizes tmux. This has several advantages compared to Foreman:
- Since procecess run inside tmux sessions, you can easily connect to specific ones, e.g.
overmind connect web. This is especially useful when interacting with a
binding.irbsession, which don’t work particularly well in Foreman.
- In the same vein, one can easily start, stop, and restart individual processes, e.g.
overmind stop sidekiqor
overmind restart web.
- Like iTerm’s tmux integration Overmind uses tmux’s control mode, so output won’t be clipped or otherwise modified.
tmuxinator is a tool for creating and managing tmux sessions. Here’s my config file for working on the DEV codebase:
name: dev root: ~/src/dev.to # Project hooks # Runs on project start, always on_project_start: > pg_ctl -D /usr/local/var/postgres start; redis-server --daemonize yes ; brew services start elasticsearch-oss # Run on project exit ( detaching from tmux session ) on_project_exit: tmux -CC attach # Run on project stop on_project_stop: > overmind quit; pg_ctl -D /usr/local/var/postgres stop; redis-cli shutdown; brew services stop elasticsearch-oss # Specifies (by name or index) which window will be selected on project startup. # If not set, the first window is used. startup_window: shell # Controls whether the tmux session should be attached to automatically. # Defaults to true. attach: false windows: - server: > bundle install && yarn && rails db:migrate && rails data_updates:run && overmind start -f Procfile.dev - logs: tail -f log/development.log - shell:
Let’s look at it in a bit more detail:
This sets the root directory for the session, so each new window will start here.
on_project_start: > pg_ctl -D /usr/local/var/postgres start; redis-server --daemonize yes ; brew services start elasticsearch-oss
Every time I start this tmux session, the above brings up all necessary services. Depending on the project I use a combination of Nix, Homebrew and manually installed tools, so this works well for me.
windows: - server: > bundle install && yarn && rails db:migrate && rails data_updates:run && overmind start -f Procfile.dev - logs: tail -f log/development.log - shell:
This section defines the three windows each new session will start with: the first runs necessary setup tasks, then uses Overmind to bring up the application. The second window tails the development logs and the last one opens a shell in the root directory.
attach: false on_project_exit: tmux -CC attach
These two options make the tmux session work nicely with the before mentioned iTerm integration: don’t automatically attach to the session when done, then attach in control mode with echo disabled (
on_project_stop: > overmind quit; pg_ctl -D /usr/local/var/postgres stop; redis-cli shutdown; brew services stop elasticsearch-oss
This stops all processes and services when exiting the session. Due to the iTerm integration all relevant tabs will be closed and the controlling session will resurface.
This is how my setup looks in action:
The combination of tmux, Overmind, and tmuxinator provides me with a feature-rich, powerful and flexible local development experience. If you got interested in this workflow keep in mind that you don’t need to adapt all tools (at once), just experiment and keep what works for you.