Simpler Dev Environments with Procfiles

Throughout the time, I've tried a number of different ways to manage my development environment. From Make files, to PM2 and Tmux.

Tmux, is well.., tmux. Tmuxinator makes it manageable, but the "layout" definition is far from readable, and copying errors from history to your clipboard, is a true pain in the ass.

I still recommend taking a look at PM2 if you need more than "get things running". It comes with a lot of options, but that also means that your config script comes with some verbosity.

This article explains how I cover my simple use cases. Those that don't require additional file watchers (see PM2), or split panes to separate logs while sticking to a single terminal (see Tmux). For those, I use Procfiles.

No fear, Procfile is here!

Procfile? Yeah, the first time I heard it I was "not another make file, right?!". And luckily, it's nothing like that. Let's get started.

So a Procfile is a simple key: command format. And much like a Dockerfile, it's by convention named after the format. I recommend creating file named Procfile in the root of your project, and commit it to your repo. If not for yourself, than for every new contributor that one day joins your team.

For MagicBell, our procfile looks as follows:

server: bin/rails s -p 3000 -e development
worker: bin/bundle exec sidekiq
webpack: bin/webpacker-dev-server

That's it. That's all the config you need to get multiple services up and running through a single command.

Obviously, we still need to install a runner to handle this procfile. Meet foreman, or one of it's forks. Foreman is a Ruby script, so for that you'll need to have Ruby installed. There are many forks though. Such as shoreman, which is a dependency free shell script, or node-foreman, which is a javascript fork. I go with node-foreman, for the simple reason that I'm a node guy and I like that I can npm install it to the dependencies of my node projects.

So, go ahead, and install node-foreman. For the sake of this howto, let's go global. Drop the -g if you have a concrete project at hand.

npm i -g node-foreman

Node-foreman installs a binary named nf. With this installed, it's as simple as running nf start in the same directory as your Procfile. Try it, and see all your services spin up and log to the same terminal. When you have an .env file in the same directory, node-foreman automatically loads all environment variables from it. If your env file is named differently, say .env.development, you can specify it with the --env flag. Use the --procfile flag if you need to specify a Procfile.

A bit more… tabs

The above might be all you need. But sometimes, I like to have my startup process a bit personalised. For that, I use scripts that I store under my home directory.

For MagicBell, my config exists of two files. One custom procfile, saved as ~/scripts/magicbell.proc:

server: bin/rails s -p 3000 -e development
worker: bin/bundle exec sidekiq
webpack: bin/webpacker-dev-server
codegen: yarn codegen -w

And the "executable" script that I run, saved as ~/scripts/start-magicbell (don't forget to chmod +x)

#!/usr/bin/env bash

cd ~/dev/magicbell/backend
ttab docker-compose up

bundle install
yarn install
yarn db:migrate

nf start -j ~/scripts/magicbell.proc -e .env.development

When read that script, you'll notice ttab. ttab is a small utility that allows us to start commands in a new terminal tab. That way, I can start postgres & redis in one terminal instance (tab) while running everything else in another. Ttab is optional, but I highly recommend using it for simplicity.

npm i -g ttab

As I've added ~/scripts to my PATH, I can get the MagicBell environment up and running using a single start-magicbell command. Let me walk you trough wat it does:

  • first, it navigates to the git repo on my drive, at ~/dev/magicbell/backend
  • runs docker-compose up in a new tab, detached from the current
  • installs all ruby gems, while the db is spinning up
  • installs all node modules
  • runs our database migration script
  • starts the four services defined in the procfile

With this single command, I've started 6 services and ran some install/migrate commands. It happened too often that I manually had to install new gems or modules after pulling changes. Those commands are fast enough to run as part of my startup, yet take enough time for the database to be online before the server starts.

That's it. Both node-foreman and ttab come with a bunch of options. So be sure to check out their readmes (especially from node-foreman!)

Liked this article?

If you made it to here, please share your thoughts on BlueSky, or leave a comment below.