Proactive Elixir Monitoring with Prometheus and PromEx
A complete guide to setting up world-class monitoring for your Elixir and Phoenix applications from day one using Prometheus and the PromEx library.
6 min read
#elixir#phoenix#prometheus#monitoring#observability#prom_ex#devops

Deploying an application without monitoring is like flying a plane without instruments. You might be flying, but you have no idea if you're about to run out of fuel or if an engine is on fire. In modern software development, observability is not an optional add-on; it's a fundamental requirement for building reliable and performant systems.

At Bitscorp, we believe in integrating monitoring from the very beginning of a project. In the Elixir ecosystem, the combination of Prometheus and the prom_ex library makes this incredibly easy and powerful.

Why Add Monitoring from Day One?

Many teams treat monitoring as an afterthought, something to be bolted on once the "real" features are built. This is a critical mistake. Here’s why you should start from day one:

  1. Establish a Performance Baseline: You can't identify a performance regression if you don't know what "normal" looks like. Early monitoring gives you a baseline for key metrics like response times, database queries, and memory usage.

  2. Catch Issues Early: Did a recent deploy introduce a memory leak or make a database query ten times slower? With proper monitoring, you'll spot these anomalies immediately, not after your users start complaining.

  3. Make Data-Driven Decisions: Instead of guessing where performance bottlenecks are, you'll have concrete data to guide your optimization efforts. This saves countless hours of premature or misdirected optimization.

  4. Build a Culture of Observability: When metrics are integrated into the development process, the entire team becomes more aware of performance and reliability. It encourages developers to think about the operational impact of their code as they write it.

The Solution: Prometheus + PromEx

Prometheus is an open-source monitoring and alerting toolkit that has become the industry standard for collecting time-series data. It works on a "pull" model, where it periodically scrapes an HTTP endpoint (usually /metrics) on your application to collect metrics.

prom_ex is a brilliant Elixir library that acts as a "batteries-included" instrumentation solution. It not only exposes your application's metrics in a Prometheus-compatible format but also bundles pre-built plugins for the most common parts of the Elixir/Phoenix stack, including:

  • The BEAM (Erlang VM)
  • Phoenix (requests, response times)
  • Ecto (database query times, connection pool usage)
  • Absinthe (GraphQL)
  • And more...

It even comes with pre-built Grafana dashboards, giving you a world-class monitoring setup with minimal effort.

Step-by-Step Integration Guide

Let's walk through how to add prom_ex to a standard Phoenix project.

Step 1: Add Dependencies

First, add prom_ex and a Prometheus-client dependency to your mix.exs:

def deps do
[
# ... other dependencies
{:prom_ex, "~> 1.9"},
# You need to choose a client. :prometheus_telemetry is a great default.
{:prometheus_telemetry, "~> 1.1"}
]
end

Then, run mix deps.get to fetch them.

Step 2: Create Your Metrics Module

prom_ex works by having you define a central metrics module. This is where you configure which plugins to use and define any custom metrics.

Create a new file at lib/my_app/metrics.ex:

defmodule MyApp.Metrics do
use PromEx, otp_app: :my_app
@impl true
def plugins do
[
# Provides BEAM memory, scheduler, and process metrics
PromEx.Plugins.Beam,
# Provides Phoenix metrics like request duration and counts
{PromEx.Plugins.Phoenix, router: MyAppWeb.Router, endpoint: MyAppWeb.Endpoint},
# Provides Ecto metrics like query times and pool usage
{PromEx.Plugins.Ecto, repos: Application.fetch_env!(:my_app, :ecto_repos)},
# If you use Oban for background jobs
# {PromEx.Plugins.Oban, oban_config: Application.fetch_env!(:my_app, Oban)}
]
end
end

Step 3: Configure prom_ex

Now, tell prom_ex about your new module in config/config.exs:

import Config
config :my_app, PromEx,
metrics_modules: [MyApp.Metrics]

Step 4: Add to Your Application's Supervision Tree

For your metrics server to start when your application starts, add it to the children list in lib/my_app/application.ex:

defmodule MyApp.Application do
use Application
@impl true
def start(_type, _args) do
children = [
# ... other children
MyApp.Metrics
]
# ...
Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
end
end

Step 5: Expose the /metrics Endpoint

Prometheus needs an endpoint to scrape. Expose it by adding the PromEx plug to your lib/my_app_web/endpoint.ex file, ideally before the Plug.RequestId plug:

defmodule MyAppWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :my_app
# ...
plug PromEx.Plug, metrics_modules: [MyApp.Metrics]
plug Plug.RequestId
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]
# ...
end

At this point, if you start your Phoenix server (mix phx.server), you can visit http://localhost:4000/metrics and see a wall of Prometheus-formatted metrics!

Creating Custom Application Metrics

The real power of monitoring comes from tracking metrics specific to your application's domain. Let's say we want to count the number of new user registrations.

First, define the metric in your MyApp.Metrics module. A Counter is perfect for this.

# In lib/my_app/metrics.ex
defmodule MyApp.Metrics do
use PromEx, otp_app: :my_app
# ... existing plugins function
@impl true
def custom_metrics() do
[
Counter.new(
name: :user_registered_total,
help: "Total number of registered users.",
labels: [:source] # Optional: e.g., [:web, :api, :import]
)
]
end
end

Now, from anywhere in your application, you can increment this counter. For example, in your Accounts context, right after a user is successfully created:

# In lib/my_app/accounts/accounts.ex
def register_user(attrs \\ %{}) do
with {:ok, %User{} = user} <-
%User{}`
|> User.registration_changeset(attrs)
|> Repo.insert() do
# Increment our custom metric!
MyApp.Metrics.increment(:user_registered_total, 1, labels: %{source: :web})
{:ok, user}
end
end

Now, every time a new user registers, your my_app_user_registered_total metric will increase, and you can track user growth in real-time.

Conclusion

With just a few lines of code, prom_ex provides a level of insight into your Elixir application that would have taken weeks to build manually. By integrating monitoring from day one, you empower your team to build more reliable, performant, and robust applications. You're no longer flying blind; you have a full dashboard of instruments to guide you.

For us at Bitscorp, prom_ex is a "day one" dependency on every new Elixir project. The value it provides in terms of operational insight and developer confidence is immeasurable.


Ready to bring world-class observability to your Elixir projects? If you need help setting up advanced monitoring, data pipelines, or building scalable Elixir applications, get in touch with us today!

© Copyright 2025 Bitscorp