Need help with erlbus?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

217 Stars 53 Forks Other 79 Commits 5 Opened issues


Simple, Distributed and Scalable PubSub Message Bus written in Erlang

Services available


Need anything else?

Contributors list

# 127,863
47 commits
# 190,020
5 commits

ErlBus Build Status

Message / Event Bus written in Erlang.

The PubSub core is a clone of the original, remarkable, and proven Phoenix PubSub Layer, but re-written in Erlang.

A new way to build soft real-time and high scalable messaging-based applications, not centralized but distributed!

Documentation can be found HERE.

See also: WEST.


ErlBus is a simple and lightweight library/tool to build messaging-based applications.

ErlBus PubSub implementation was taken from Phoenix Framework, which provides an amazing, scalable and proven PubSub solution. In addition to this, ErlBus provides an usable and simpler interface on top of this implementation.

You can read more about the PubSub implementation HERE.



In your

{deps, [
  {ebus, "0.2.2", {pkg, erlbus}}


In your

def deps do
    {:ebus, "~> 0.2", hex: :erlbus}

Getting Started

Assuming you have a working Erlang installation (18 or later), building ErlBus should be as simple as:

$ git clone
$ cd erlbus
$ make

Quick Start Example

Start an Erlang console with

$ make shell

Once into the erlang console:

% subscribe the current shell process
ebus:sub(self(), "foo").

% spawn a process Pid = spawn_link(fun() -> timer:sleep(infinity) end). <0.57.0>

% subscribe spawned PID ebus:sub(Pid, "foo"). ok

% publish a message ebus:pub("foo", {foo, "hi"}). ok

% check received message for Pid ebus_proc:messages(Pid). [{foo,"hi"}]

% check received message for self ebus_proc:messages(self()). [{foo,"hi"}]

% unsubscribe self ebus:unsub(self(), "foo"). ok

% publish other message ebus:pub("foo", {foo, "hello"}). ok

% check received message for Pid ebus_proc:messages(Pid). [{foo,"hi"},{foo,"hello"}]

% check received message for self (last message didn't arrive) ebus_proc:messages(self()). [{foo,"hi"}]

% check subscribers (only Pid should be in the returned list) ebus:subscribers("foo"). [<0.57.0>]

% check topics ebus:topics(). [<>]

% subscribe self to other topic ebus:sub(self(), "bar"). ok

% check topics ebus:topics(). [<>,<>]

% publish other message ebus:pub("bar", {bar, "hi bar"}). ok

% check received message for Pid (last message didn't arrive) ebus_proc:messages(Pid). [{foo,"hi"},{foo,"hello"}]

% check received message for self ebus_proc:messages(self()). [{foo,"hi"},{bar,"hi bar"}]


  • You may have noticed that is not necessary additional steps/calls to create/delete a topic, this is automatically handled by
    , so you don't worry about it!

Now, let's make it more fun, start two Erlang consoles, first one:

$ erl -name [email protected] -setcookie ebus -pa _build/default/lib/*/ebin -s ebus -config test/test.config

The second one:

$ erl -name [email protected] -setcookie ebus -pa _build/default/lib/*/ebin -s ebus -config test/test.config

Then what we need to do is put these Erlang nodes in cluster, so from any of them send a ping to the other:

% From node1 ping node2
net_adm:ping('[email protected]').

Excellent, we have both nodes in cluster, thanks to the beauty of Distributed Erlang. So, let's repeat the above exercise but now in two nodes.

In the

create a handler and subscription to some topic:
% create a callback fun to use ebus_proc utility
CB1 = fun(Msg) ->
  io:format("CB1: ~p~n", [Msg])

% other callback but receiving additional arguments, % which may be used when message arrives CB2 = fun(Msg, Args) -> io:format("CB2: Msg: p, Args: ~pn", [Msg, Args]) end. #Fun<erl_eval.12.54118792>

% use ebus_proc utility to spawn a handler H1 = ebus_proc:spawn_handler(CB1). <0.70.0> H2 = ebus_proc:spawn_handler(CB2, ["any_ctx"]). <0.72.0>

% subscribe handlers ebus:sub(H1, "foo"). ok ebus:sub(H2, "foo"). ok </erl_eval.12.54118792></erl_eval.6.54118792>

Repeat the same thing above in


Once you have handlers subscribed to the same channel in both nodes, publish some messages from any node:

% publish message
ebus:pub("foo", {foo, "again"}).
CB1: {foo,"again"}
CB2: Msg: {foo,"again"}, Args: "any_ctx"

And in the other node you will see those messages have arrived too:

CB1: {foo,"again"}
CB2: Msg: {foo,"again"}, Args: "any_ctx"

Let's check subscribers, so from any Erlang console:

% returns local and remote subscribers

You can also check the TESTS for more info about to use


So far, so good! Let's continue!

Point-To-Point Example

The great thing here is that you don't need something special to implement a point-to-point behavior. It is as simple as this:

ebus:dispatch("topic1", #{payload => "M1"}).

Dispatch function gets the subscribers and then picks one of them to send the message out. You can provide a dispatch function to pick up a subscriber, otherwise, a default function is provided (picks a subscriber random).

Dispatch function comes in 3 different flavors:

  • ebus:dispatch/2
    : receives the topic and the message.
  • ebus:dispatch/3
    : receives the topic, message and a list of options.
  • ebus:dispatch/4
    : same as previous but receives as 1st argument the name of the server, which is placed by default in the other functions.

Dispatch options are:

  • {scope, local | global}
    : allows you to choose if you want to pick a local subscriber o any. Default value:
  • {dispatch_fun, fun(([term()]) -> term())}
    : function to pick up a subscriber. If it isn't provided, a default random function is provided.

To see how this function is implemented go HERE.

Let's see an example:

% subscribe local process
ebus:sub(self(), "foo").

% spawn a process Pid = spawn_link(fun() -> timer:sleep(infinity) end). <0.57.0>

% subscribe spawned PID ebus:sub(Pid, "foo"). ok

% check that we have two subscribers ebus:subscribers("foo"). [<0.57.0>,<0.38.0>]

% now dispatch a message (default dispatch fun and scope) ebus:dispatch("foo", #{payload => foo}). ok

% check that only one subscriber received the message ebus_proc:messages(self()). [#{payload => foo}] ebus_proc:messages(Pid). []

% dispatch with options Fun = fun([H | _]) -> H end. #Fun<erl_eval.6.54118792> ebus:dispatch("foo", <>, [{scope, global}, {dispatch_fun, Fun}]). ok

% check again ebus_proc:messages(self()). [#{payload => foo}] ebus_proc:messages(Pid). [<>] </erl_eval.6.54118792>

Extremely easy isn't?

Distributed ErlBus

ErlBus is distributed by nature, it doesn't require any additional/magical thing.

Once you have an Erlang cluster, messages are broadcasted using PG2, which is the default PubSub adapter. Remember, it's a Phoenix PubSub clone, so the architecture and design it's the same.

Phoenix Channels are supported on PubSub layer, which is the core. Take a look at this blog post.


See examples.

Running Tests

$ make test

Building Edoc

$ make doc

Note: Once you run previous command, a new folder

is created, and you'll have a pretty nice HTML documentation.

ErlBus Profiles

So far, the only additional profile provided is

, because
profile is enough to do all build and test tasks.

Debug Profile

ErlBus gives you the chance to compile and run

in debug profile. In this mode, additional monitoring, debug and testing dependencies will be fetched:
  • recon: Collection of functions and scripts to debug Erlang in production.
  • eper: Collection of performance related tools (

To run

with debug profile enabled:
$ make REBAR_PROFILE=debug shell

Now you can use

like you want in order to monitor and debug

Change Log

All notable changes to this project will be documented in the

Copyright and License

Original work Copyright (c) 2014 Chris McCord

Modified work Copyright (c) 2016 Carlos Andres Bolaños

ErlBus source code is licensed under the MIT License.

NOTE:: Pub/Sub implementation was taken from Phoenix Framework.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.