Start/Stop Mailpit with CommandBox Server Start/Stop

I use Mailpit to capture email while developing CFML applications locally. It works great, but I don’t necessarily need it running all the time. Since I already use CommandBox to start and stop my local ColdFusion server, I wanted Mailpit to follow the same lifecycle.

Fortunately, CommandBox server scripts make this straightforward. When the CommandBox server starts, it can start Mailpit. When the server stops, it can stop Mailpit as well.

Prerequisites

This example is for macOS and assumes Mailpit was installed with Homebrew:

brew install mailpit

You can test Mailpit manually with:

brew services start mailpit
brew services stop mailpit

By default, Mailpit provides:

CommandBox server lifecycle scripts

CommandBox supports server-specific scripts in server.json. Among the available lifecycle events are:

  • onServerStart, which runs while the server is starting
  • onServerStop, which runs before the server stops

Add the following top-level scripts object to the CommandBox server configuration:

{
    "scripts": {
        "onServerStart": "!brew services start mailpit",
        "onServerStop": "!brew services stop mailpit"
    }
}

The ! prefix tells CommandBox to execute a native operating-system command.

If CommandBox cannot find Homebrew in its PATH, use the full path to the executable. On an Apple Silicon Mac, that will commonly be /opt/homebrew/bin/brew:

{
    "scripts": {
        "onServerStart": "!/opt/homebrew/bin/brew services start mailpit",
        "onServerStop": "!/opt/homebrew/bin/brew services stop mailpit"
    }
}

On an Intel Mac, Homebrew is commonly located at /usr/local/bin/brew. Run which brew in Terminal to confirm the correct path.

Wait until Mailpit is ready

Starting the Homebrew service does not necessarily mean Mailpit is ready to accept requests at that exact instant. Mailpit provides /readyz for checking its readiness. A successful request returns an HTTP 200 response.

We can make the startup script check that endpoint once per second for up to 20 seconds:

{
    "scripts": {
        "onServerStart": [
            "!brew services start mailpit",
            "!for i in {1..20}; do curl -fsS http://127.0.0.1:8025/readyz && exit 0; sleep 1; done; echo 'Mailpit failed to become ready' >&2; exit 1"
        ],
        "onServerStop": "!brew services stop mailpit"
    }
}

This script:

  1. Starts Mailpit through Homebrew.
  2. Checks the Mailpit readiness endpoint.
  3. Continues as soon as Mailpit responds successfully.
  4. Returns an error if Mailpit does not become ready within 20 seconds.

You can also check Mailpit manually at any time:

curl -fsS http://127.0.0.1:8025/readyz

To inspect the Homebrew service state instead, run:

brew services info mailpit

Show Mailpit’s status on a local home page

My CommandBox server hosts several local applications, so I have a simple home page that links to each application and development resource. I wanted its Mailpit card to show whether Mailpit was online.

When Mailpit is stopped, the card displays a red Offline badge:

Mailpit Offline
Mailpit Offline

When Mailpit is available, it displays a green Online badge:

Mailpit Online
Mailpit Online

First, check the readiness endpoint near the beginning of the CFML page, before the HTML output:

<cfset mailpitOnline = false>

<cftry>
    <cfhttp
        url="http://127.0.0.1:8025/readyz"
        method="GET"
        timeout="2"
        result="mailpitHealth">

    <cfset mailpitOnline = val(mailpitHealth.statusCode) eq 200>

    <cfcatch type="any">
        <cfset mailpitOnline = false>
    </cfcatch>
</cftry>

The default value is false. If Mailpit responds with HTTP status 200, it changes to true. A timeout or connection error is caught so an unavailable Mailpit service does not cause the home page itself to fail.

The two-second timeout also prevents the check from delaying the page for too long.

Next, use that value to set the card’s border and status badge:

<div class="col-6 col-md-4 col-lg-3">
    <a href="http://localhost:8025" class="text-decoration-none" target="_blank">
        <div class="card h-100 shadow-sm <cfif mailpitOnline>border-success<cfelse>border-danger</cfif>">
            <div class="card-body text-center">
                <h5 class="card-title text-secondary">
                    Mailpit
                    <cfif mailpitOnline>
                        <span class="badge text-bg-success align-middle"
                              style="font-size: 0.6rem;">Online</span>
                    <cfelse>
                        <span class="badge text-bg-danger align-middle"
                              style="font-size: 0.6rem;">Offline</span>
                    </cfif>
                </h5>
            </div>
        </div>
    </a>
</div>

This example uses Bootstrap 5 classes for the card, border, and badge styling.

Putting it all together

The final CommandBox configuration is:

"scripts": {
    "onServerStart": [
        "!brew services start mailpit",
        "!for i in {1..20}; do curl -fsS http://127.0.0.1:8025/readyz && exit 0; sleep 1; done; echo 'Mailpit failed to become ready' >&2; exit 1"
    ],
    "onServerStop": "!brew services stop mailpit"
}

Now Mailpit starts along with the CommandBox server, its readiness is verified, and the local home page indicates whether it is online. When the CommandBox server is stopped normally, Mailpit is stopped as well.

A few things to keep in mind

  • The stop hook runs when the server is stopped through CommandBox. It cannot run if CommandBox is force-terminated or the computer shuts down unexpectedly.
  • Homebrew services are user-wide. If several CommandBox servers or other applications share the same Mailpit instance, stopping one server could stop Mailpit while something else is using it.
  • If one CommandBox server hosts several applications, put the scripts in that server’s shared configuration. Mailpit will then follow the lifecycle of the entire server rather than any single application.
  • The status badge reflects Mailpit’s state when the CFML page is rendered. Refresh the page to update it.

That’s it. Mailpit is available when the local development server is running and gets out of the way when development is finished.

Add items (like Mailpit or tailing logs) to the CommandBox Tray

I’ve always been a fan of the tray that’s available when CommandBox launches. On the Mac, it appears as a menu bar icon in the top menu (first icon below).

CommandBox menu bar icon
CommandBox menu bar icon

It has some very convenient built-in options:

Servername
├── Stop Server
├── Restart Server
├── Open...
│   ├── Webroot
│   ├── Server Home
│   ├── Site Home
│   └── Server Admin
├── Info
│   ├── Engine: adobe 2023.0.17+330864
│   ├── Webroot: /Path/to/website/
│   ├── URL: https://127.0.0.1:8443
│   ├── PID: 99999
│   └── Heap: Not set

I frequently need to access Mailpit and various log files as I work through the development process on a current project. This got me wondering if I could add some of my own frequently accessed items to the tray. It turns out you can add quite a few different things.

Enable the tray

First, enable the tray in the server.json file for the site.

"trayEnable": true,

Next, let’s start simple and add a single item to open Mailpit in a browser. The trayOptions setting accepts an array of objects. Each object should contain a label, an action, and one of [url, path, command]. The label is the text that appears in the tray. The action tells CommandBox what to perform (openbrowseropenfilesystemrunAsync, etc.). The [url, path, command] values provide the URL of the site, the filesystem path, or the command to execute, respectively.

Setting the tray options

"trayOptions": [
    {
        "label": "Mailpit Web Interface",
        "action": "openbrowser",
        "url": "http://localhost:8025"
    }
]

Let’s dive a little deeper with some other options.

Add a “divider” of blank space between CommandBox’s built-in options and the ones we are adding.

{
    "label": " ",
    "disabled": true
}

Open a folder on the filesystem.

{
    "label": "Open Log Folder",
    "action": "openfilesystem",
    "path": "${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs"
}

Tail the last 100 lines of mailsent.log (macOS only as written, but adaptable for Linux or Windows).

{
    "label": "Tail mailsent.log",
    "action": "runAsync",
    "command": "tmpfile=$(mktemp -t tail-mailsent).command; printf '#!/bin/zsh\nprintf \"\\e]0;Tail mailsent.log\\a\"\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/mailsent.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""
}

Explanation of the tail command

Add some icons using emojis.

🛠 Development Tools
📬 Mailpit Web Interface
📂 Logs

Putting it all together

CommandBox tray open
CommandBox tray open
"trayEnable": true,
"trayOptions": [
    {
        "label": " ",
        "disabled": true
    },
    {
        "label": "🛠 Development Tools",
        "items": [
            {
                "label": "📬 Mailpit Web Interface",
                "action": "openbrowser",
                "url": "http://localhost:8025"
            },
            {
                "label": "📂 Logs",
                "items": [
                    {
                        "label": "Open Log Folder",
                        "action": "openfilesystem",
                        "path": "${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs"
                    },
                    {
                        "label": "Tail application.log",
                        "action": "runAsync",
                        "command": "tmpfile=$(mktemp -t tail-application).command; printf '#!/bin/zsh\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/application.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""
                    },
                    {
                        "label": "Tail exception.log",
                        "action": "runAsync",
                        "command": "tmpfile=$(mktemp -t tail-exception).command; printf '#!/bin/zsh\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/exception.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""
                    },
                    {
                        "label": "Tail mailsent.log",
                        "action": "runAsync",
                        "command": "tmpfile=$(mktemp -t tail-mailsent).command; printf '#!/bin/zsh\nprintf \"\\e]0;Tail mailsent.log\\a\"\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/mailsent.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""
                    }
                ]
            }
        ]
    }
]

Explanation of the tail command

This command creates a temporary shell script, makes it executable, and opens it in Terminal. Reminder: this version is macOS only as written because it uses zshmktemp, and open -a Terminal.

"command": "tmpfile=$(mktemp -t tail-mailsent).command; printf '#!/bin/zsh\nprintf \"\\e]0;Tail mailsent.log\\a\"\nclear\ntail -n 100 -f \"%s\"\n' '${serverinfo.serverHomeDirectory}/WEB-INF/cfusion/logs/mailsent.log' > \"$tmpfile\"; chmod +x \"$tmpfile\"; open -a Terminal \"$tmpfile\""

What it does

  • mktemp -t tail-mailsent
    • Creates a uniquely named temporary file.
  • printf '#!/bin/zsh ...'
    • Writes a small shell script into the temporary file.
  • printf "\e]0;Tail mailsent.log\a"
    • Sets the Terminal window title.
  • clear
    • Clears the Terminal window before output begins.
  • tail -n 100 -f
    • Displays the last 100 lines of the log file and continues following new entries in real time.
  • chmod +x
    • Makes the temporary script executable.
  • open -a Terminal
    • Opens the script in the macOS Terminal application.

Next Steps

There are all kinds of other tasks that could be added to the tray such as:

  • Ping a server
  • Flushing DNS
  • Open the hosts file to edit
  • Open the site’s Git repo

Good luck!

Mailpit + ColdFusion Local Email Testing

Overview

This article documents how to configure Adobe ColdFusion to use Mailpit for local email testing.

Mailpit captures outbound email locally so messages can be reviewed in a browser without sending real email.


Export Existing Mail Settings (Optional)

Before making changes, export the current ColdFusion mail configuration so it can be restored later if needed.

cfconfig export from=server.local to=mailSettings.json includeList=mailservers**

Example exported configuration:

{
    "mailServers": [
        {
            "tls": true,
            "password": "SUPERSECRET",
            "port": 587,
            "username": "USERNAME",
            "ssl": false,
            "smtp": "smtp.mailgun.org"
        }
    ]
}

Install Mailpit

Install Mailpit using Homebrew.

brew install mailpit

Start Mailpit

Start the Mailpit service.

brew services start mailpit

Configure ColdFusion to Use Mailpit

Method 1: Configure with CommandBox

Create a file named mailSettingsMailpit.json.

{
    "mailServers": [
        {
            "tls": false,
            "password": "",
            "port": 1025,
            "username": "",
            "ssl": false,
            "smtp": "127.0.0.1"
        }
    ]
}

Import the Mailpit configuration into ColdFusion.

cfconfig import from=mailSettingsMailpit.json to=server.local

Method 2: Configure in ColdFusion Administrator

Navigate to:

ColdFusion Administrator → Server Settings → Mail

Configure the following values:

Mail Server: 127.0.0.1
Server Port: 1025
Username: blank
Password: blank
Use TLS: unchecked
Use SSL: unchecked

Test Email Delivery

Create a .cfm test page.

<cfscript>
    recipientCount = 5;

    for (i = 1; i <= recipientCount; i++) {

        recipient = "foo#i#@bar.com";

        cfmail(
            to = recipient,
            from = "[email protected]",
            subject = "Mailpit Test",
            type = "html"
        ) {

            writeOutput("
                <h1>Hello from ColdFusion</h1>
                <p>
                    It is #dateFormat(now(), 'mm/dd/yyyy')# at
                    #dateTimeFormat(now(), 'hh:mm:ss tt')#.
                    This should show up in Mailpit.
                </p>
            ");
        }

        sleep(3000);

        writeOutput("<p>Mail sent.</p>");
    }

    writeOutput('<p><a href="#cgi.script_name#">Run again</a></p>');
</cfscript>

Verify Email Delivery

Open the Mailpit web interface:

http://localhost:8025

The test emails should appear in the Mailpit inbox.

Mailpit inbox
Mailpit inbox

Restore Original Mail Settings (Optional)

Restore the original ColdFusion mail configuration.

cfconfig import from=mailSettings.json to=server.local

Restart ColdFusion after restoring the configuration if required.


Stop Mailpit

Stop the Mailpit service.

brew services stop mailpit