Rails quick tips #6: tmux, tmuxinator and Overmind

- ruby rails tips

In today’s Rails quick tip we’ll look at my local development workflow, specifically tmux, Overmind, and tmuxinator.

tmux

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:

tmux screenshot

While this is already pretty cool, tmux really shines in combination with iTerm’s tmux integration.

iTerm settings

The two highlighted settings ensure that

  1. each tmux window becomes a separate tab in the attaching window and
  2. that the attaching tab gets “buried” (hidden) until the end of the session.

Overmind

Overmind is a process manager for Procfile-based applications that utilizes tmux. This has several advantages compared to Foreman:

  1. 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.pry or binding.irb session, which don’t work particularly well in Foreman.
  2. In the same vein, one can easily start, stop, and restart individual processes, e.g. overmind stop sidekiq or overmind restart web.
  3. Like iTerm’s tmux integration Overmind uses tmux’s control mode, so output won’t be clipped or otherwise modified.

tmuxinator

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:

root: ~/src/dev.to

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 (-CC).

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:

Summary

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.

Comments powered by Talkyard.