Local SMTP+IMAP email server for Elixir dev/test environments. Wraps GreenMail — catches all outbound email at the SMTP level. No emails leave your machine. Ever.

Why not Swoosh.Adapters.Local?

Because it only catches emails sent through Swoosh. If your app talks SMTP directly (a SmtpClient, a background mailer, a third-party library), Swoosh.Local never sees those messages. GreenMail catches everything — it's a real SMTP server listening on localhost.

Setup

1. Add dependency

# mix.exs
defp deps do
  [
    {:greenmail, "~> 0.1", only: :dev}
  ]
end

2. Install the JAR

mix greenmail.install

Downloads greenmail-standalone.jar to ~/.greenmail/. One download, shared by all projects.

Requires: Java 17+ (brew install openjdk on macOS)

3. Add to supervision tree (dev only)

# application.ex
def start(_type, _args) do
  children = [
    # ... your children ...
  ] ++ greenmail_children()

  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end

defp greenmail_children do
  if Application.get_env(:my_app, :greenmail, false) do
    [{GreenMail.Server, [supervisor: MyApp.Supervisor]}]
  else
    []
  end
end
# config/dev.exs
config :my_app, greenmail: true

4. Point SMTP at GreenMail

Any SMTP config pointing at localhost:3025 will be caught. For IMAP ingestion, use localhost:3143.

Auth is disabled — any username/password works and auto-provisions a mailbox for that address.

API

GreenMail.running?()  # => true
GreenMail.status()    # => {:ok, %{smtp_port: 3025, imap_port: 3143, os_pid: 12345, ...}}
GreenMail.stop()      # Stop server, free ports
GreenMail.start()     # Restart after stop
GreenMail.restart()   # Flush all mailboxes

Ports

ProtocolPort
SMTP3025
IMAP3143

How it works

  • GenServer starts GreenMail JAR as an Erlang port process
  • Java process dies when your app dies — no orphans
  • Per-address mailboxes: email to alice@example.com lands in that IMAP inbox
  • In-memory only: emails vanish on restart
  • If ports are already in use, assumes external mail server and skips startup

Reading caught emails

Use any IMAP client. From Elixir, you can use :gen_smtp or Mail:

# The email sent to alice@example.com is in that address's IMAP inbox
# Login: alice@example.com / any-password
# Host: localhost, Port: 3143, SSL: off

Or just use a mail client (Thunderbird, etc.) pointed at localhost:3143.

License

MIT