How To Setup Your Development Environment for Ruby on Rails in 2021

Learn how to make asdf, Overmind, Postgres and Docker into a hyper-productive Rails setup.

By Mike Rogers Wednesday, Mar 10, 2021

Ruby on Rails is a fantastic framework for building lovely web applications in 2021, mostly because out the box it ships with the majority of the parts (e.g. Web Sockets & Mailers) you’ll need to build a modern applications.

One thing holding Ruby on Rails back is that historically Ruby has been quite difficult to install, and a lot of internet commentators have held onto their negative experiences. However, over the last few years the tooling has drastically improved & getting an enjoyable setup is now a fairly painless experience.

In this post, I’m going to cover the tools asdf, Postgres.app, Overmind & Docker which I use to achieve an easy to setup & low maintenance, local Ruby on Rails development environment.

Setting up on MacOS

If you’re using MacOS you’re in for a treat, a lot of Rails developers use either MacOS or Linux, so the tooling is generally really fantastic.

As for Linux & Windows, while some of the tools do somewhat work, I’d suggest skipping straight to using Docker (The next section) as it’ll work without much setup required on those operating systems.

Installing Ruby with asdf

In the past Rubyists have used RVM, RBenv or chruby to install Ruby. They’re good, but more recently I’ve seen lots of Rubyists switching to asdf.

Asdf is similar to the other tools I listed above, but it allows you to install & manage multiple languages, along with changing the version used on a per folder basis. This means you can have a single tool, and be able to install everything from Ruby through to NPM & Yarn.

Once you have asdf installed, you can use the ruby plugin to easily manage the ruby version on your machine.

# Add the Ruby Plugin
$ asdf plugin-add ruby
# Install Ruby 3.0
$ asdf install ruby 3.0.0
# Use Ruby 3.0 by default
$ asdf global ruby 3.0.0
# Reshim, i.e. point the default ruby executable to the asdf one
$ asdf reshim ruby

With these 4 commands, you can have Ruby up and running on your machine in no time.

Installing Postgres Database

Postgres is the most popular database choice when working with Ruby on Rails. I used to install it via Homebrew, but I found two solutions which are much better.

When I need a very specific version of Postgres which for a particular project (E.g. a legacy project), I’ll often use Docker to pull down a image matching the version I want & then I’ll run that via Docker-Compose as a stand-alone service. However, if Docker isn’t your thing I’ve also become a fan of Postgres.app.

Postgres.app is GUI Mac app for managing Postgres Databases. What I like about it, is it brings back memories of when I used to use MAMP, which offered a really nice way to just start & stop the database locally.

I started using Postgres.app and I really found it super low effort to maintain. While I love being able to control services via terminal, having the GUI app for something I only need to restart occasionally made me a very happy developer.

Turning everything on with Overmind

When I started using Rails, I would turn on my application using rails server which worked pretty well, but over time my applications started to become more complex, so I needed to pass in environmental variables (Via a .env file) and also turn on multiple extra services like Sidekiq & Webpacker.

Initially I solved this problem by using a Ruby Gem called Foreman, which would read the Procfile within my project & turn on the defined services. The awesome thing about this approach was Heroku also reads this file & turns on services in production based on it, which is a nice way to keep development & production the same.

More recently, I was shown Overmind which is very similar to Foreman but it runs each service in a TMUX session which means you can start every service, then connect & restart individual services as you require.

# You can run everything in the Procfile with:
$ overmind start
# You can run individual services
$ overmind start -l web

Both Foreman & Overmind are fantastic, so either one you choose is a great choice. One fun benefit of both of these gems is they will read the .env file in the directory you execute the command & inject the environmental variables into your app.

Often this .env file isn’t committed to version control, but instead a .env.sample file is committed instead. I use the sample file to document on where to find the values (e.g. API keys) which are set in the .env file.

When you should use this approach

This approach is perfect for small projects and when you’re learning Ruby on Rails, but it will also work for larger projects where you just need Ruby and a database to do your work.

One of the drawbacks of this approach is when you need a specific version of Postgres, or your app has non-standard libraries which need to be installed on the developers machine. I’ve also found this approach cumbersome when locally a developer needs to turn on quite a few extra services to experience the full entire application.

What about Docker?

Once your application becomes sufficiently complicated to setup, or requires some very specific versions of software to run, you should consider looking into Docker.

Docker is a tool which allows you to create a perfect consistent environment for running your application. You can think of it as putting your app in a box, where you define everything your application needs to run (e.g. Ruby, Yarn, Redis & Postgres). You then take that box, create a snapshot of what it looks like, then share that snapshot with your developers.

How do I use Docker with Rails?

After you’ve installed the Docker desktop application, you’ll have access to a bunch of new terminal commands which will allow you to build images, or download images from Docker Hub.

In the Rails world, I’ve found that most projects have their own custom Dockerfile, which is used to define what the virtual machine which runs the Rails application has installed.

My go-to default Rails Dockerfile has grown a lot over time, but it covers a lot of the common Docker + Rails use cases.

With that Dockerfile, it installs all the packages required to run Ruby on Rails & connect to Postgres. To avoid rebuilding the image every time I want to change a gem, I use Docker-Compose to share a volume containing the installed gems & yarn packages. Often I can just drop it into projects without modification & jump straight into development.

What is Docker-Compose?

Docker-Compose allows us to turn on multiple services which our app requires, and share volumes between these services, using a YAML file & a single command. It’s really powerful.

When I’ve taken on Rails projects, I will make sure the services match what is running in production on Heroku. So if I’m using Heroku Postgres with version 12.3, that’s the service I make sure every developer is developing the application with locally.

In my go-to docker-compose.yml file, I based it on th services I often have on Heroku (Postgres & Redis), which I then to the version I have running in production. Using this setup, I’m able to turn on my entire app with a single terminal command:

$ docker-compose up

When you should you use Docker?

I’m a big fan of Docker, it’s really perfect for when any type of project becomes complicated or you just want to encourage your team to have a consistent setup.

One of the big downsides of Docker is it does require a lot of machine resources on MacOS, so your developers will need the latest machines with at least 16GB of memory.

I’ve been experimenting with using Docker for local development on an Linux machine & I’ve had a pretty good experience with only 8GB of RAM. So I suspect the future of MacOS development with Docker could end up being done on a Linux cloud machine.

Summary

I’m really excited about the future of Ruby on Rails development! I think having these easier options for getting up and going, will help bring Ruby on Rails to more developers & give them really fantastic first impressions.

One thing I have started doing lately which is quite fun, is using Docker for just the services like Postgres & Redis, then running my Ruby on Rails app via asdf. The big advantage to this setup is I can lock down the exact version number of Postgres I’m using, while getting the performance benefits of running Ruby natively.