Catch your emails in Rails development with Mailhog.
The thing about emails in Rails development mode
Almost every non-trivial Rails application needs to handle email. You may need to send email verification messages, password reset emails, or even notify your users of internal events within your application. Email notifications are ubiquitous.
With ActionMailer being a part of the Rails framework, Rails has our back on this. It's easy to configure and offers a wide range of features, so there's nothing to complain about.
But one thing that bothers us, and probably many other Rails developers, is the lack of a proper interface for testing emails in development. What does the email actually look like with the user I just signed up with? And did it actually send the email? While we should have tests for that, nothing compares to a real manual click-through experience. How do we actually configure ActionMailer in development to not accidentely send mails to the wrong person?
Even though Rails ships with Mailer Previews, those previews are a one time snapshot of your template. This is great for designing and coding your template, but it’s completely decoupled from the underlying business process. And you better check that as a developer!
ActionMailer uses sendmail
as the default for sending emails. However, using this method on a misconfigured development machine can be highly dangerous. It is also not recommended to use a well-known private SMTP server (such as Gmail or Outlook) in this case.
Instead, developers have long been using mail catchers for development purposes. There are various gems available, such as LetterOpener, Mailcatcher, and even commercial solutions like Mailtrap. These solutions address the long-standing pain point for developers of checking emails in Rails development. Typically, you define an outgoing smtp_settings
hash for the development environment and point the mailer to these services.
Mailhog to the rescue
Another approach that we highly recommend is using Mailhog. It offers several benefits over other solutions:
- It runs locally
- It operates as a separate process independent of the application, allowing for easy usage
- It does not store any emails as it uses an in-memory store
- It is fast
- It runs on every platform
- No need for an additional gem
On a Mac, you can easily install Mailhog by following these steps:
brew install mailhog
and then be started with mailhog
which opens the SMTP server on port 1025
and the http server with its nice UI on port 8025
.
A quick example
To quickly define an example email for testing purposes, let's configure ActionMailer to use Mailhog in the development environment. We can accomplish this by creating an initializer file called smtp_settings.rb.
if Rails.env.development?
ActionMailer::Base.smtp_settings = {
address: "localhost",
port: 1025
}
ActionMailer::Base.delivery_method = :smtp
end
Create a mailer class with a mailer action of your choice.
class UserMailer < ApplicationMailer
def welcome
mail(to: "user@example.com", from: "info@the-next-big-thing.com", subject: "Welcome Mail") do |format|
format.html
format.text
end
end
end
Let's define a simple html template for the action in app/user_mailer/welcome.html.erb:
<h1>Welcome to The Next Big Thing</h1>
And a corresponding text template in app/user_mailer/welcome.text.erb:
Welcome to The Next Big Thing
Make sure Mailhog is running, and then open the Rails console (rails console) to send a test email.
UserMailer.welcome.deliver
Now open your browser and navigate to http://0.0.0.0:8025/. Here, you can view the actual email that was sent from your development environment and exclusively captured by Mailhog.
Why is there no default in Rails?
While we really love Mailhog for its ease of setup, there is still one question: Wouldn't it be great to have something like this by default in Rails?.
With Rails 8 finally moving towards more sensible defaults to make development easier (see the milestone list for Rails 8), we think it would be very useful to have a standard way to catch mails in development. This would be especially helpful for beginners who might struggle with the initial configuration.
Dreaming should be allowed, so let's add it to the wish list for the coming and promising years of Rails!