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.

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:

I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!

Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.

You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days.

### 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: ### 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: 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 . 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.