This is a text-only version of the following page on https://raymii.org:
---
Title       : 	Set up a federated XMPP Chat Network with ejabberd, your own Google Talk Hangouts alternative
Author      : 	Remy van Elst
Date        : 	11-06-2013
URL         : 	https://raymii.org/s/tutorials/Set_up_a_federated_XMPP_Chat_Network_with_ejabberd.html
Format      : 	Markdown/HTML
---



This tutorial shows you how to set up your own federated chat network using
ejabberd. It covers a basic single node ejabberd server and also the setup of an
ejabberd cluster, including errors and DNS SRV record examples. Last but not
least federation is also covered.

<p class="ad"> <b>Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called  Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p>


### Why set up your own XMPP server

There are a few reasons to set up your own XMPP server.

You might use Google Talk or as it now is named Hangouts. Google's service
recently changed and it is going to drop XMPP compatibility. If you have non-
gmail chat contacts you can keep chatting to them. And still use an open
protocol which is widely supported, not being locked in to google specific
software and hardware.

Or you might want to have more control over the logging of your data. Turn of
ejabberd logging and use [Off The Record][2] which gives you full privacy (and
perfect forward secrecy).

You might want to use awesome multi-account chatting applications like
[Pidgin][3], [Psi+][4], [Empathy][5], [Adium][6], iChat/Messages or Miranda IM.
And on Android you can use [Xabber][7], [Beem][8] or [OneTeam][9]. Did you know
that big players like Facebook, WhatsApp and Google (used) to use XMPP as their
primary chat protocol?

Or you might be a sysadmin in need of an internal chat solution. I've got a
ejabberd cluster running for a client consisting of 4 Debian 7 VM's (2GB RAM
each) spread over 3 sites and 1 datacenter, serving 12000 total users and most
of the time 6000 concurrently.

XMPP is an awesome and extendible protocol, on which you can find more here:
<https://en.wikipedia.org/wiki/XMPP>

### Information

This setup is tested on Debian 7, Ubuntu 12.04 and 10.04 and OS X 10.8 Server,
all running `ejabberd` installed via the package manager, either `apt` or
`ports`. It also works on Windows Server 2012 with the `ejabberd` compiled from
the `erlang` source but that is not covered in this tutorial.

This tutorial uses the `example.org` domain as the chat domain, and the server
`chat.example.org` as the xmpp server domain. For the clustering part the
servers `srv1.example.org` and `srv2.example.org` are used. Replace these values
for your setup.

### Single node / master node ejabberd installation

If you want to set up a single node installation of ejabberd, e.g. no
clustering, then follow only this part and the DNS part of the tutorial. If you
want to set up a cluster, then also follow this part and continue with the next
part.

#### Installing Ejabberd

This is simple, use your package manager to install ejabberd:

    
    
    apt-get install ejabberd
    

You will also install a few dependencies for the erlang runtime.

#### Configuring ejabberd

We are going to configure the `ejabberd` service. First stop it:

    
    
    /etc/init.d/ejabberd stop
    

Now use your favorite text editor to edit the config files. The ejabberd config
is erlang config, so comments are not `#` but `%%`. Also, every config option
ends with a dot `(.)`.

    
    
    vim /etc/ejabberd/ejabberd.cfg
    

First we are going to add our chat domain name:

    
    
    {hosts, ["example.org"]}.
    

If you want more domains then you add them as shown below:

    
    
    {hosts, ["sparklingclouds.nl", "raymii.org", "sparklingnetwork.nl"]}.
    

This domain name is not the name of the servers you are adding.

Next we define an admin user:

    
    
    {acl, admin, {user, "remy", "example.org"}}.
    

`remy` corresponds with the part before the `@` in the XMPP ID, and
`example.org` with the part after. If you need more admin users, add another ACL
line.

Now if you want people to be able to register via their XMPP client enable in
band registration:

    
    
    {access, register, [{allow, all}]}.
    

If you are using MySQL or LDAP authentication then you wouldn't enable this.

I like to have a shared roster with roster groups, and some clients of mine use
a shared roster with everybody so that nobody has to add contacts but they see
all online users, enable the mod _shared_ roster:

    
    
    %% Do this in the modules block
      {mod_shared_roster,[]},
    

If you are pleased with the config file, save it and restart ejabberd:

    
    
    /etc/init.d/ejabberd restart
    

We now need to register a user to test our setup. If you've enabled in-band
registration you can use your XMPP client, and if you did not enable in-band
registration you can use the `ejabberdctl` command:

    
    
    ejabberdctl register remy example.org 'passw0rd'
    

Now test it using an XMPP client like Pidgin, Psi+ or Empathy. If you can
connect, then you can continue with the tutorial. If you cannot connect, check
your ejabberd logs, firewall setting and such to troubleshoot it.

### Clustering ejabberd

Note that you have to have a correctly working master node to continue with the
ejabberd clustering. If your master node is not working then fix that first.

Important: the modules you use should be the same on every cluster node. If you
use LDAP/MySQL authentication, or a shared_roster, or special MUC settings, or
offline messaging, for the clustering this does not matter as long as it is on
all nodes.

So lets get started. We are first going to configure the master node, and then
the slave nodes.

#### Prepare the master node

Stop the ejabberd server on the master and edit the `/etc/default/ejabberd`
file:

    
    
    vim /etc/default/ejabberd
    

Uncomment the hostname option and change it to a FQDN hostname:

    
    
    ERLANG_NODE=ejabberd@srv1.example.org
    

And add the external (public) IP addres as a tuple (no dots but comma's):

    
    
    INET_DIST_INTERFACE={20,30,10,5}
    

If you use ejabberd internally then use the primary NIC address.

We are going to remove all the mnesia tables. They will be rebuilt with an
ejabberd restart. This is way easier then changing the mnesia data itself. Don't
do this on a already configured node without backing up the erlang cookie.

First backup the erlang cookie:

    
    
    cp /var/lib/ejabberd/.erlang.cookie ~/
    

Then remove the mnesia database:

    
    
    rm /var/lib/ejabberd/*
    

And restore the erlang cookie:

    
    
    cp ~/.erlang.cookie /var/lib/ejabberd/.erlang.cookie
    

To make sure all erlang processes are stopped kill all processes from the
ejabberd user. This is not needed but the `epmd` supervisor process might still
be running:

    
    
    killall -u ejabberd
    

And start ejabberd again:

    
    
    /etc/init.d/ejabberd start 
    

If you can still connect and chat, then continue with the next part, configuring
the slave nodes.

#### Prepare the slave nodes

*A slave node should first be configured and working as described in the first part of this tutorial. You can copy the config files from the master node. *

Stop the ejabberd server:

    
    
    /etc/init.d/ejabberd stop
    

Stop the ejabberd server on the master and edit the `/etc/default/ejabberd`
file:

    
    
    vim /etc/default/ejabberd
    

Uncomment the hostname option and change it to a FQDN hostname:

    
    
    ERLANG_NODE=ejabberd@srv2.example.org
    

And add the external (public) IP addres as a tuple (no dots but comma's):

    
    
    INET_DIST_INTERFACE={30,40,20,6}
    

If you use ejabberd internally then use the primary NIC address.

Now remove all the mnesia tables:

    
    
    rm /var/lib/ejabberd/*
    

Copy the cookie from the ejabberd master node, either by `cat` and `vim` or via
`scp`:

    
    
    # On the master node
    cat /var/lib/ejabberd/.erlang.cookie
    HFHHGYYEHF362GG1GF
    
    # On the slave node
    echo "HFHHGYYEHF362GG1GF" > /var/lib/ejabberd/.erlang.cookie
    chown ejabberd:ejabberd /var/lib/ejabberd/.erlang.cookie
    

We are now going to add and compile an erlang module, the easy_cluster module.
This is a very small module which adds an erlang shell command to make the
cluster addition easier. You can also execute the commands in the erlang
functions itself on an erlang debug shell, but I find this easier and it gives
less errors:

    
    
    vim /usr/lib/ejabberd/ebin/easy_cluster.erl
    

Add the following contents:

    
    
    -module(easy_cluster).
    
    -export([test_node/1,join/1]).
    
    test_node(MasterNode) ->
        case net_adm:ping(MasterNode) of 'pong' ->
            io:format("server is reachable.~n");
        _ ->
            io:format("server could NOT be reached.~n")
        end.
    
    join(MasterNode) ->
        application:stop(ejabberd),
        mnesia:stop(),
        mnesia:delete_schema([node()]),
        mnesia:start(),
        mnesia:change_config(extra_db_nodes, [MasterNode]),
        mnesia:change_table_copy_type(schema, node(), disc_copies),
        application:start(ejabberd).
    

Save it and compile it into a working erlang module:

    
    
    cd /usr/lib/ejabberd/ebin/
    erlc easy_cluster.erl
    

Now check if it succeeded:

    
    
    ls | grep easy_cluster.beam
    

If you see the file it worked. You can find more info on the module here:
<https://github.com/chadillac/ejabberd-easy_cluster/>

We are now going to join the cluster node to the master node. Make sure the
master is working and running. Also make sure the erlang cookies are
synchronized.

On the slave node, start an ejabberd live shell:

    
    
    /etc/init.d/ejabberd live
    

This will start an erlang shell and it will give some output. If it stops
outputting then you can press `ENTER` to get a prompt. Enter the following
command to test if the master node can be reached:

    
    
    easy_cluster:test_node('ejabberd@srv1.example.org').
    

You should get the following response: `server is reachable`. If so, continue.

Enter the following command to actually join the node:

    
    
    easy_cluster:join('ejabberd@srv1.example.org').
    

Here's example output from a successful test and join join:

    
    
    /etc/init.d/ejabberd live
    *******************************************************
    * To quit, press Ctrl-g then enter q and press Return *
    *******************************************************
    
    Erlang R15B01 (erts-5.9.1) [source] [async-threads:0] [kernel-poll:false]
    
    Eshell V5.9.1  (abort with ^G)
    
    =INFO REPORT==== 10-Jun-2013::20:38:15 ===
    I(<0.39.0>:cyrsasl_digest:44) : FQDN used to check DIGEST-MD5 SASL authentication: "srv2.example.org"
    
    =INFO REPORT==== 10-Jun-2013::20:38:15 ===
    I(<0.576.0>:ejabberd_listener:166) : Reusing listening port for 5222
    
    =INFO REPORT==== 10-Jun-2013::20:38:15 ===
    I(<0.577.0>:ejabberd_listener:166) : Reusing listening port for 5269
    
    =INFO REPORT==== 10-Jun-2013::20:38:15 ===
    I(<0.578.0>:ejabberd_listener:166) : Reusing listening port for 5280
    
    =INFO REPORT==== 10-Jun-2013::20:38:15 ===
    I(<0.39.0>:ejabberd_app:72) : ejabberd 2.1.10 is started in the node 'ejabberd@srv2.example.org'
    easy_cluster:test_node('ejabberd@srv1.example.org').
    server is reachable.
    ok
    (ejabberd@srv2.example.org)2> easy_cluster:join('ejabberd@srv1.example.org').
    
    =INFO REPORT==== 10-Jun-2013::20:38:51 ===
    I(<0.39.0>:ejabberd_app:89) : ejabberd 2.1.10 is stopped in the node 'ejabberd@srv2.example.org'
    
    =INFO REPORT==== 10-Jun-2013::20:38:51 ===
        application: ejabberd
        exited: stopped
        type: temporary
    
    =INFO REPORT==== 10-Jun-2013::20:38:51 ===
        application: mnesia
        exited: stopped
        type: permanent
    
    =INFO REPORT==== 10-Jun-2013::20:38:52 ===
    I(<0.628.0>:cyrsasl_digest:44) : FQDN used to check DIGEST-MD5 SASL authentication: "srv2.example.org"
    
    =INFO REPORT==== 10-Jun-2013::20:38:53 ===
    I(<0.1026.0>:ejabberd_listener:166) : Reusing listening port for 5222
    
    =INFO REPORT==== 10-Jun-2013::20:38:53 ===
    I(<0.1027.0>:ejabberd_listener:166) : Reusing listening port for 5269
    
    =INFO REPORT==== 10-Jun-2013::20:38:53 ===
    I(<0.1028.0>:ejabberd_listener:166) : Reusing listening port for 5280
    ok
    (ejabberd@srv2.example.org)3>
    =INFO REPORT==== 10-Jun-2013::20:38:53 ===
    I(<0.628.0>:ejabberd_app:72) : ejabberd 2.1.10 is started in the node 'ejabberd@srv2.example.org'
    

Exit your erlang shell by pressing `CTRL+C` twice. Now stop ejabberd and start
it again:

    
    
    /etc/init.d/ejabberd restart
    

You can now check in the admin webinterface if the cluster join succeeded:

    
    
    http://srv1.example.org:5280/admin/nodes/
    

![Ejabberd nodes][10]

If it shows the other node you are finished. If not, see if the steps worked and
check the below section on troubleshooting.

Repeat the above steps for every node you want to add. You can add as many nodes
as you want.

### Errors when clustering

When setting up your cluster you might run into errors. Below are my notes for
the errors I found.

  * ejabberd restart does not restart epmd (erlang daemon)

    * overkill solution: killall -u ejabberd
  * ejabberd gives hostname errors

    * make sure the hostname is set correctly (`hostname srv1.example.com`)
  * ejabberd gives inconsistent database errors

    * backup the erlang cookie (`/var/lib/ejabberd/.erlang.cookie`) and then remove the contents of the `/var/lib/ejabberd` folder so that mnesia rebuilds its tables.
  * ejabberd reports "Connection attempt from disallowed node"

    * make sure the erlang cookie is correct (`/var/lib/ejabberd/.erlang.cookie`). Set vim in insert mode before pasting...

### DNS SRV Records and Federation

The DNS SRV Record is used both by chat clients to find the right server address
as well as by other XMPP servers for federation. Example: Alice configures her
XMPP clients with the email address alice@example.org. Her chat client looks up
the SRV record and knows the chat server to connect to is `chat.example.org`.
Bob sets up his client with the address `bob@bobsbussiness.com`, and adds Alice
as a contact. The XMPP server at `bobsbussiness.com` looks up the SRV record and
knows that it should initiate a server2server connection to `chat.example.org`
to federate and let Bob connect with Alice.

The BIND 9 config looks like this:

    
    
    ; XMPP
    _xmpp-client._tcp                       IN SRV 5 0 5222 chat.example.org.
    _xmpp-server._tcp                       IN SRV 5 0 5269 chat.example.org.
    _jabber._tcp                            IN SRV 5 0 5269 chat.example.org.
    

It is your basic SRV record, both the client port and the server2server port,
and legacy Jabber. If you have hosted DNS then either enter it in your panel or
consult your service provider.

You can use the following `dig` query to verify your SRV records:

    
    
    dig _xmpp-client._tcp.example.org SRV
    dig _xmpp-server._tcp.example.org SRV
    

Or if you are on Windows and have to use `nslookup`:

    
    
    nslookup -querytype=SRV _xmpp-client._tcp.example.org
    nslookup -querytype=SRV _xmpp-server._tcp.example.org
    

If you get a result like this then you are set up correctly:

    
    
    ;; QUESTION SECTION:
    ;_xmpp-client._tcp.raymii.org.  IN      SRV
    
    ;; ANSWER SECTION:
    _xmpp-client._tcp.raymii.org. 3600 IN   SRV     5 0 5222 chat.raymii.org.
    

The actual record for `chat.raymii.org` in my case are multiple A records:

    
    
    ;; ADDITIONAL SECTION:
    chat.raymii.org.        3600    IN      A       84.200.77.167
    chat.raymii.org.        3600    IN      A       205.185.117.74
    chat.raymii.org.        3600    IN      A       205.185.124.11
    

But if you run a single node this can also be a CNAME or just one A/AAAA record.

### Final testing

To test if it all worked you can add the Duck Duck Go XMPP bot. If this works
flawlessly and you can add it and chat to it, then you have done everything
correctly. The email address to add is `im@ddg.gg`.

   [1]: https://www.digitalocean.com/?refcode=7435ae6b8212
   [2]: http://otr.cypherpunks.ca/
   [3]: http://pidgin.im/
   [4]: http://psi-im.org/
   [5]: https://live.gnome.org/Empathy
   [6]: http://adium.im/
   [7]: http://www.xabber.com/
   [8]: http://beem-project.com/
   [9]: http://oneteam.im/
   [10]: https://raymii.org/s/inc/img/ejabberd1.png

---

License:
All the text on this website is free as in freedom unless stated otherwise. 
This means you can use it in any way you want, you can copy it, change it 
the way you like and republish it, as long as you release the (modified) 
content under the same license to give others the same freedoms you've got 
and place my name and a link to this site with the article as source.

This site uses Google Analytics for statistics and Google Adwords for 
advertisements. You are tracked and Google knows everything about you. 
Use an adblocker like ublock-origin if you don't want it.

All the code on this website is licensed under the GNU GPL v3 license 
unless already licensed under a license which does not allows this form 
of licensing or if another license is stated on that page / in that software:

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Just to be clear, the information on this website is for meant for educational 
purposes and you use it at your own risk. I do not take responsibility if you 
screw something up. Use common sense, do not 'rm -rf /' as root for example. 
If you have any questions then do not hesitate to contact me.

See https://raymii.org/s/static/About.html for details.