Building A Real-Time Messaging App With Erlang & Ejabberd

real time chat and messaging app using ejabberd erlang
Request Demo

69 million WhatsApp messages are sent globally as you read this sentence. Messages on the app don’t freeze and calls don’t stagger.

How does the chat app handle so much data without performance issues? 

Erlang, the functional programming language, is one key reason. 

Additionally, Erlang works even better when paired with ejabberd, an XMPP server.

In this article, I’ll show you how to use Erlang + ejabberd to build a complete messaging app like WhatsApp. 

Key Takeaways

  • Erlang helps apps handle millions of users, restart failed processes, and run with almost no downtime.
  • Ejabberd server ensures instant message delivery, security, and smooth performance.
  • Erlang + ejabberd together create a fast, secure, and crash-resistant messaging app.
  • Use ejabberd for an open-source approach or MirrorFly for a ready-made, customizable solution.
  • MirrorFly is easier with extensive features and customisable security. ejabberd is free but needs more setup work.

What is Erlang?

Erlang is a functional programming language and runtime system designed for building concurrent, distributed, and fault-tolerant apps. It is very reliable, meaning if one part of the system fails, the rest keeps working.

Erlang is not just highly available. You can use it for: 

  • Handling millions of simultaneous connections with lightweight processes. 
  • Restart failed processes with built-in supervision trees.
  • Build your chat app on a native server with distributed computing.
  • Handle frequent message exchanges with messaging passing (Actor model).
  • Push updates and fixes to a live chat app with minimal downtime. 

💡Fact Time
Apps built with Erlang achieve extremely high availability (99.999% uptime), meaning they can run for 20 years with less than one second of total downtime

What Is Ejabberd?

ejabberd is an open-source, scalable XMPP (Extensible Messaging and Presence Protocol) server that’s written in Erlang. It supports real-time messaging, presence updates, group chats, and security features like encryption and authentication.

Since ejabberd is built in Erlang, it shares most features as the runtime environment itself. However, it extends its capabilities a bit more with XMPP-based messaging features.

What other apps can you build with XMPP? Read here

Here’s what makes ejabberd a powerful real-time chat server:

  • Takes care of real-time chat, presence updates, and message delivery features in a chat app.
  • Balances the load across multiple servers with a ready-to-use clustering solution.
  • Uses WebSockets and HTTP binding (BOSH) to enable real-time messaging in browsers. 
  • You can build chat history feature with XEP-0313 (Message Archive Management).
  • Stores and manages data with open-source database integrations like PostgreSQL, MySQL, MariaDB, and LDAP.

How Erlang And Ejabberd Work Together?

When a user sends a message, Ejabberd processes it quickly using Erlang’s power and delivers it in real time. If a user is offline, Ejabberd stores the message and sends it when they come back. 

Erlang also helps Ejabberd run on multiple servers, so even if one part fails, the system keeps working.

Look into the flow of the chat app process:

Ejabberd can send and receive messages without downtime, even across multiple servers, because Erlang helps it work smoothly on many servers at once (distributed system). 

If one server or part of the system crashes, Erlang automatically fixes it and keeps everything running (fault tolerance). 

This allows Ejabberd to handle large numbers of users without interruptions (clustering & load balancing).

On the other hand, Ejabberd takes care of the security and authentication with encryption features like TLS encryption and OAuth. It combines with built-in real-time messaging features supported by Erlang to establish millions of secure, simultaneous connection (Concurrency) to initiate the message flow. 

Thereby, when a user sends a message, Ejabberd quickly processes it using Erlang, then delivers it instantly to the recipient using XMPP. If the recipient is offline, Ejabberd saves the message and sends it later when they come online.

Building A Messaging App In Erlang + Ejabberd

There are 2 ways to build a messaging app in Erlang. 

  1. ejabberd (Self-Hosted XMPP Server) – A fully open-source approach.
  2. MirrorFly (Self-Hosted Solution) – A custom ready-made API/SDK solution. 

Building A Chat App From Scratch On ejabberd Self-Hosted Setup (Open-Source XMPP Server)

🚀 Best for: Developers who want full control over chat infrastructure.
Pros: Fully open-source, scalable, customizable.Cons: Requires setup and maintenance.
🚀 Best for Developers who want full control over their chat infrastructure.
Pros
Fully open-sourceScalableCustomizable
Cons
Time consumingCode-heavyRequires maintenance & setup

Here’s a step-by-step breakdown to help you implement your Erlang chat application in a structured way:

Step 1: Install Erlang

Make sure Erlang is installed on your machine. Run the following command to check:

erl

If Erlang is installed correctly, you will see the interactive Erlang shell.

Step 2: Set Up the Project Structure

Create the following Erlang modules:

  • chat_server.erl (Handles client connections)
  • chat_client.erl (Represents the chat client)
  • chat_fsm.erl (Handles chat state transitions)
  • chat_app.erl (Supervises the application)

Step 3: Implement the Chat Client

The chat client is a gen_server responsible for sending and receiving messages.

1. Create chat_client.erl

-module(chat_client).
-behaviour(gen_server).

-export([start_link/1, send_message/2]).
-export([init/1, handle_call/3]).

-record(state, {handler_pid}).

start_link(Name) ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [{name, Name}], []).

init(Args) ->
    {value, {_, Name}} = lists:keysearch(name, 1, Args),
    io:fwrite("Chat client started with name: ~p~n", [Name]),
    case gen_server:call({global, chat_server}, {register, Name}) of
        {ok, HandlerPid} -> {ok, #state{handler_pid = HandlerPid}};
        {error, Reason} -> 
            io:fwrite("Chat client terminated: ~p~n", [Reason]),
            {stop, normal}
    end.

send_message(Name, Message) ->
    gen_server:call(?MODULE, {send, {Name, Message}}).

handle_call({send, {Name, Message}}, _From, State) ->
    HandlerPid = State#state.handler_pid,
    Reply = gen_fsm:send_event(HandlerPid, {send, {Name, Message}}),
    {reply, Reply, State}.

Step 4: Implement the Chat Server

The chat server is a gen_server responsible for handling client connections.

2. Create chat_server.erl

-module(chat_server).
-behaviour(gen_server).

-export([start_link/0]).
-export([init/1, handle_call/3]).

-record(state, {clients = []}).

start_link() ->
    gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).

init([]) ->
    io:fwrite("Chat server started...~n"),
    {ok, #state{}}.

handle_call({register, Name}, {ClientPid, _Tag}, State) ->
    Clients = State#state.clients,
    case gen_fsm:start_link(chat_fsm, [{clients, Clients}, {name, Name}, {client_pid, ClientPid}], []) of
        {ok, Pid} ->
            NewState = State#state{clients = [{Name, Pid} | Clients]},
            lists:foreach(fun({_, UserPid}) -> gen_fsm:send_all_state_event(UserPid, {join, {Name, Pid}}) end, Clients),
            {reply, {ok, Pid}, NewState};
        {error, Reason} ->
            io:fwrite("Failed to start FSM: ~p~n", [Reason]),
            {stop, normal, State}
    end.

Step 5: Implement the Chat Handler (FSM)

Each client connection has an FSM that handles messages and client states.

3. Create chat_fsm.erl

-module(chat_fsm).
-behaviour(gen_fsm).

-export([start_link/3]).
-export([init/1, connected/2]).

-record(state, {clients, client_pid, name}).

start_link(Clients, Name, ClientPid) ->
    gen_fsm:start_link(?MODULE, [{clients, Clients}, {name, Name}, {client_pid, ClientPid}], []).

init(Args) ->
    Clients = proplists:get_value(clients, Args),
    Name = proplists:get_value(name, Args),
    ClientPid = proplists:get_value(client_pid, Args),
    {ok, connected, #state{clients = Clients, name = Name, client_pid = ClientPid}}.

connected({send, {ReceiverName, Message}}, State) ->
    Clients = State#state.clients,
    SenderName = State#state.name,
    case proplists:get_value(ReceiverName, Clients) of
        undefined -> {error, no_client};
        HandlerPid -> gen_fsm:send_event(HandlerPid, {receive, {SenderName, Message}})
    end,
    {next_state, connected, State}.

connected({receive, {SenderName, Message}}, State) ->
    ClientPid = State#state.client_pid,
    gen_server:call(ClientPid, {receive, {SenderName, Message}}),
    {next_state, connected, State}.

connected(_Event, State) ->
    {next_state, connected, State}.

Step 6: Implement the Chat Supervisor

The supervisor ensures the chat server and FSM processes remain running.

4. Create chat_app.erl

-module(chat_app).
-behaviour(application).

-export([start/2, stop/1]).

start(_Type, _Args) ->
    chat_sup:start_link().

stop(_State) ->
    ok.

Step 7: Start the Chat Application

  1. Compile the code
    Run the following command inside your project directory:
erlc *.erl

2. Start the Erlang shell

erl -s chat_app

3. Start the Chat Server

chat_server:start_link()

4. Start a Chat Client

chat_client:start_link("Alice").
chat_client:start_link("Bob").

5. Send Messages


chat_client:send_message("Bob", "Hello Bob!")

Your Next Steps:

  • Implement message persistence using Mnesia.
  • Add a command-line interface for easier interaction.
  • Enhance the FSM with multiple states (e.g., offline, online, typing).

This should give you a structured way to build and extend your Erlang chat system. 

2. Building A Messaging App With MirrorFly Self-Hosted Chat Solution

Before we jump into the development steps with MirrorFly, here’s how MirrorFly + Erlang + ejabberd are related:

MirrorFly’s self-hosted solution is built on ejabberd, which runs on Erlang. Here’s how they are connected:

  1. MirrorFly Uses ejabberd as Its Core
  • ejabberd is a powerful chat server that supports real-time messaging.
  • MirrorFly improves it by adding 1000s of features like voice/video calls, push notifications, and better scalability.
  1. ejabberd is Built with Erlang
  • Erlang makes ejabberd fast, stable, and able to handle millions of users at once.
  • It ensures the chat system keeps running smoothly even if some parts fail.
  1. MirrorFly Simplifies Everything
  • Instead of setting up and coding ejabberd yourself, MirrorFly provides:
    ✅ Ready-to-use chat server
    ✅ APIs & SDKs for easy app integration
    ✅ An admin dashboard to manage everything

In Simple Terms:

  • MirrorFly Self-Hosted = ejabberd (chat server) + 1000s of messaging Features + Complete development in 48 hrs. 
  • ejabberd runs on Erlang, which makes it fast and reliable.
  • You don’t need to learn Erlang. MirrorFly handles all the technical parts for you!

Ready to build with MirrorFly? In this section, we’ll talk you through the steps for developing a messaging platform with MirrorFly chat solution. 

🚀 Best for 100% customization, full data control, custom security and on-premise hosting. 
Pros
Fully open-sourceScalableCustomizable
Cons
Time consumingCode-heavyRequires maintenance & setup

Here’s a step-by-step guide to integrating the MirrorFly Chat SDK into your Android app:

Step 1: Set Up Your Project

  • Create a new project or open an existing one in Android Studio.
  • Ensure your environment meets the following requirements:
    • Android 5.0+ (API Level 21 or above)
    • Java 7+
    • Gradle 4.1.0+
    • targetSdkVersion 34 (if using SDK 7.11.4+)

Step 2: Get Your License Key

  • Contact MirrorFly team and register for a MirrorFly account.
  • Log in to your account.
  • Find the License Key in the Application Info section.

Step 3: Add Dependencies

Modify Gradle files:

  1. For Gradle 6.8+ (Add this to settings.gradle):
dependencyResolutionManagement {
    repositories {
        mavenCentral()
        google()
        jcenter()
        maven {
            url "https://repo.mirrorfly.com/release"
        }
    }
}

2. Add MirrorFly SDK to app/build.gradle:

dependencies {
    implementation 'com.mirrorfly.sdk:mirrorflysdk:7.13.13'
}

3. Enable Jetifier in gradle.properties:

android.enableJetifier=true

4. Add permissions in AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET"/>

Step 4: Initialize SDK

Inside your Application class (MyApplication.java):

ChatManager.initializeSDK("YOUR_LICENSE_KEY", (isSuccess, throwable, data) -> {
    if (isSuccess) {
        Log.d("TAG", "SDK Initialized Successfully");
    } else {
        Log.d("TAG", "SDK Initialization Failed: " + data.get("message"));
    }
});

Add MyApplication to AndroidManifest.xml:

<application
    android:name=".MyApplication"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">
</application>

Step 5: Register a User

FlyCore.registerUser("USER_IDENTIFIER", (isSuccess, throwable, data) -> {
    if (isSuccess) {
        String userJid = (String) data.get("userJid");
        Log.d("TAG", "User Registered: " + userJid);
    } else {
        Log.e("TAG", "User Registration Failed");
    }
});

💡 USER_IDENTIFIER must be alphanumeric, hyphen (-), or underscore (_) only.

Step 6: Connect to Chat Server

At this stage, you can either connect to MirrorFly’s multi-tenant chat server or your own private server or datacenter. 

ChatManager.setConnectionListener(new ChatConnectionListener() {
    @Override
    public void onConnected() {
        Log.d("TAG", "Connected to Chat Server");
    }

    @Override
    public void onDisconnected() {
        Log.d("TAG", "Disconnected from Chat Server");
    }
});

Step 7: Send and Receive Messages

📩 Send a Message

TextMessage textMessage = new TextMessage();
textMessage.setToId("RECIPIENT_JID");
textMessage.setMessageText("Hello!");

FlyMessenger.sendTextMessage(textMessage, (isSuccess, error, chatMessage) -> {
    if (isSuccess) {
        Log.d("TAG", "Message Sent Successfully");
    }
});

📥 Receive Messages

ChatEventsManager.setupMessageEventListener(new MessageEventsListener() {
    @Override
    public void onMessageReceived(@NotNull ChatMessage message) {
        Log.d("TAG", "New Message: " + message.getMessageText());
    }
});

For adding features like WebRTC video calling and SIP/ VoIP calling, refer to MirrorFly’s API documentation. 


Choosing The Right Solution For Building Chat App

Both the solutions are reliable to build a chat app in Erlang, with ejabberd server. However, if you need to choose one, here is a comparison that will help. 

FeatureMirrorFly Self-Hosted Solutionejabberd Self-Hosted
Customization⭐⭐⭐⭐⭐⭐⭐⭐⭐
Data Control⭐⭐⭐⭐⭐⭐⭐⭐⭐
1000+ Features⭐⭐⭐⭐⭐⭐⭐
White-labeling⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Ease of Setup⭐⭐⭐⭐
Scalability⭐⭐⭐⭐⭐⭐⭐
Security⭐⭐⭐⭐⭐⭐⭐⭐⭐
Cost💰💰 (One-time fee)Free (Open-Source)

Overall, we’ll recommend you to go for MirrorFly self-hosted solution if you need 100% customization and data control and choose building from scratch on an ejabberd server if you prefer an open-source approach. 

The decision is completely up to you. 

If you’d like to get some help with making the decision, contact this expert team and they’ll get in touch with you in the next 24 hrs. 

Ready To Add Instant Messaging Features In The Next 48 Hrs?

MirrorFly comes with 1000s of features and 100% customization – And we can’t wait to see what you build!

Talk to us
  • 200+ Happy Clients
  • Unlimited Instant Messaging
  • Multi-user Support

Frequently Asked Questions

What is Ejabberd, and why is it suitable for building a chat app?

Ejabberd is a Erlang XMPP server software that handles real-time messaging. It’s ideal for building chat messaging app because it provides ready-made infrastructure for message routing, storage, and error handling across platforms. Instead of building these complex systems from scratch, developers can use ejabberd as a foundation and focus on building their client-side applications.

What are the advantages of using Erlang for chat app development?

Erlang is excellent for real-time messaging app because it offers high performance with low latency, reliable message delivery, and handles concurrent user connections efficiently. Its built-in support for fault tolerance keeps the system running smoothly, while its lightweight processes make development and maintenance simpler compared to other languages.

What’s needed to build an Ejabberd/Erlang chat app?

To build an Ejabberd/Erlang chat app, you need to install Erlang, MySQL database, and Ejabberd server software along with several system dependencies. The core setup consists of an Ejabberd chat server for message handling and a WebRTC signaling server for connections. You’ll also need to configure Ejabberd for authentication and database integration, while following proper code style guidelines during development.

How do you install and configure Ejabberd for a chat messaging app?

To build a one to one chat app using ejabberd & Erlang, follow the steps of this Ejabberd tutorial 

Download and extract Ejabberd on your Ubuntu server
Install required dependencies
Install Ejabberd and verify the installation
Modify the configuration file for your needs
Start the Ejabberd server
Test the connection via localhost to ensure it’s running properly

Related Articles

Rajeshwari

Rajeshwari is a skilled digital marketer, passionate about SEO and exploring the latest trends and tech innovations in communication and Chat APIs. With a keen eye for detail, she helps brands improve their online visibility, and she is always eager to stay ahead in the evolving digital landscape.

10 Comments

    • Two major reasons:

      i) No straightforward API available for sending push notification from one device to another (without the use of a separate backend).

      ii) No inbuilt encryption, you would have to implement your own encryption for chatting.

      Reply
    • List of libraries that we’ll use to build the chat app:

      firebase – the client-side libraries for applications that use Firebase services
      react – a JavaScript library for building user interfaces
      react-native – a framework for building native apps with React
      redux ‒ a state management library for JavaScript applications
      eslint ‒ a linting utility for JavaScript applications
      relative-date – JavaScript module for outputting relative dates

      Reply
  1. The tutorial which is more helpful for the new developer to build a one to one chat app. Thanks for sharing the article!

    Reply
  2. Hey Alex,
    Nice post. I am developing a one to one chat application in Android. This blog helped me and also clarified the doubts.
    Looking for further updates.

    Reply
  3. Other members upon accepting the request can have private chat with them. … We’ll be making use of AngularJS 1 for this tutorial. Download the AngularJS …. There are many ways I could improve this application.

    Reply

Leave a Comment