ejabberd

This document is still a work in progress!

ejabberd is a high performance XMPP server easily scalable to large public servers, but not too heavy for self-hosting. It can be installed from Linux distribution repositories relatively easily and the default configuration is suitable for a reasonably well working XMPP server. However, these default settings tend to be quite conservative and if you want to have better support for modern XMPP features it requires some tweaking. The official documentation for all the ejabberd module settings can be found here.

Prerequisites:

  • A domain name
  • A server

This tutorial begins with a single virtualhost2. For example purposes, example.net is the domain. Replace all instances of example.net with your own domain.

DNS A/AAAA records and SRV records are required for each service on the XMPP server.

A records:

  • example.net
  • muc.example.net (for group chats)
  • upload.example.net (for HTTP file upload)
  • pubsub.example.net (for the pubsub node)
  • proxy.example.net (for file transfer proxy)
  • turn.example.net (for STUN/TURN)

Each pointing to the IP address of the server that is going to run ejabberd.

Create SRV records for each, pointing to a domain that resolves to the server, as so:

(All records are in the form _service._proto.name IN SRV priority weight port target)

1_xmpp-client._tcp   IN SRV  5 0 5222 example.net.
2_xmpps-client._tcp  IN SRV  5 0 5223 example.net.
3_xmpp-server._tcp   IN SRV  5 0 5269 example.net.
4_xmpps-server._tcp  IN SRV  5 0 5270 example.net.

and

1_xmpp-client._tcp.muc   IN SRV  5 0 5222 example.net.
2_xmpps-client._tcp.muc  IN SRV  5 0 5223 example.net.
3_xmpp-server._tcp.muc   IN SRV  5 0 5269 example.net.
4_xmpps-server._tcp.muc  IN SRV  5 0 5270 example.net.

for each of the subdomains (starting with muc). Exclude turn.example.net.

Lastly, add one set of SRV records, for STUN/TURN.

1_stun._udp   IN SRV  5 0 3478 turn.example.net.
2_stun._tcp   IN SRV  5 0 3478 turn.example.net.
3_stuns._tcp  IN SRV  5 0 5349 turn.example.net.
4
5_turn._udp   IN SRV  5 0 3478 turn.example.net.
6_turn._tcp   IN SRV  5 0 3478 turn.example.net.
7_turns._tcp  IN SRV  5 0 5349 turn.example.net.

Extra info: as a result of these SRV delegation records, hosting XMPP on a server other than the one at example.net is an option (i.e. if splitting services on one domain across servers). Further info can be found at XEP-0368.

Open ports:

TCP: 80 443 5222 5223 5269 5270 3478 5349 49152-65535

UDP: 3478 49152-65535

80 & 443 are for the web server, 5222, 5223, 5269 and 5270 are for XMPP, and the rest are for STUN/TURN.

  • get (an) SSL certificate(s) for your domain, as well as several subdomains, all in all:

    • example.net
    • muc.example.net
    • upload.example.net
    • pubsub.example.net
    • proxy.example.net
    • turn.example.net
  • proxypass http://127.0.0.1:5443 through to:

  • make sure that for /xmpp you have what is necessary to proxy websockets too. If you're using nginx, increase client_max_body_size for HTTP uploads.

  • make sure the certificate files are readable and/or in a place that is readable by the ejabberd account.

To avoid using something like nginx + Certbot, use the built-in ejabberd acme module, but this article assumes it is desired to host other web services on the same system, in which case each HTTP service would be reverse proxied to a single HTTPS web service.

Finally install the system package. Ensure the build has PostgreSQL support.

Confirm the file /etc/ejabberd/ejabberd.yml exists, and is readable by the account that runs ejabberd (almost definitely ejabberd), by, if necessary, copying over the example ejabberd.yml or wget/curling it from the github repo. If it is obtained from the repo, make sure the version corresponds to the version of ejabberd packaged by your OS.

A separate database should be created for each virtualhost, as this makes things clearer, and in addition, easier to migrate individual virtualhosts in the future. However, the ability to use only one, as described here, now exists as well.

Follow standard PostgreSQL installation instructions for your OS. Once this has been done, connect to the database as an admin and:

  1. create an ejabberd database account with CREATE USER ejabberd WITH PASSWORD 'your_password';. Don't forget to change the password, and note it down.
  2. create a database for the virtualhost with CREATE DATABASE ejabberd_example OWNER ejabberd;. Replace example with something corresponding to the virtualhost.
  3. quit the psql shell, and import the database schema from GitHub with the command curl -s https://raw.githubusercontent.com/processone/ejabberd/master/sql/pg.sql | sudo -u ejabberd psql ejabberd_example (once again replace example).

Begin by replacing localhost under hosts with the virtualhost (e.g. example.net), then list the certfiles previously obtained under certfiles.

1hosts:
2  - example.net
3
4certfiles:
5  - "/etc/ejabberd/certs/*/*"

Now set default_db: sql at the root level of the YAML file. This should be followed by host_config and the database configuration for your virtualhost, as shown below. Customise each value to according to the setup.

 1host_config:
 2  example.net:
 3    sql_type: pgsql
 4    sql_server: "localhost"
 5    sql_port: 5432
 6    sql_username: "ejabberd"
 7    sql_password: "postgres_password"
 8    sql_database: "ejabberd_example"
 9    auth_method: sql
10    auth_password_format: scram

Under listen, ensure all the correct services are enabled on each port, including s2s TLS on port 5270 (not default):

 1listen:
 2  -
 3    port: 5222
 4    module: ejabberd_c2s
 5    max_stanza_size: 262144
 6    shaper: c2s_shaper
 7    access: c2s
 8    starttls_required: true
 9  -
10    port: 5223
11    tls: true
12    module: ejabberd_c2s
13    max_stanza_size: 262144
14    shaper: c2s_shaper
15    access: c2s
16    starttls_required: true
17  -
18    port: 5269
19    module: ejabberd_s2s_in
20    max_stanza_size: 524288
21  -
22    port: 5270
23    tls: true
24    module: ejabberd_s2s_in
25    max_stanza_size: 524288

Next, enable the HTTP server and the STUN/TURN server modules. Set turn_ipv4_address and ip to the server's IPv4 address. TLS will be off for the HTTP server as it is reverse proxied through the previously set-up web server.

 1  -
 2    port: 5443
 3    module: ejabberd_http
 4    request_handlers:
 5      /xmpp/admin: ejabberd_web_admin
 6      /xmpp/bosh: mod_bosh
 7      /xmpp/upload: mod_http_upload
 8      /xmpp/ws: ejabberd_http_ws
 9      /.well-known/host-meta: mod_host_meta
10      /.well-known/host-meta.json: mod_host_meta
11  -
12    port: 3478
13    transport: udp
14    module: ejabberd_stun
15    use_turn: true
16    turn_min_port: 49152
17    turn_max_port: 65535
18    # The server's public IPv4 address:
19    turn_ipv4_address: 0.0.0.0
20  -
21    port: 5349
22    transport: tcp
23    module: ejabberd_stun
24    use_turn: true
25    tls: true
26    turn_min_port: 49152
27    turn_max_port: 65535
28    ip: 0.0.0.0
29    turn_ipv4_address: 0.0.0.0

Set s2s_use_starttls: required at the root.

At this point it is possible to set up some ACLs. acls are just the access control lists, set up access_rules corresponding to your needs, which will be what are passed to module settings. At the minimum an admin account is recommended. Example:

 1acl:
 2  admin:
 3    user: juliet@example.net
 4  capulet:
 5    - user: juliet@example.net
 6    - user: tybalt@example.net
 7  nurse:
 8    - user: angelica@example.net
 9
10access_rules:
11  household:
12    allow: capulet
13    allow: nurse

Add abuse addresses under mod_disco. It is also possible to add other contact addresses according to XEP-0157:

1modules:
2# ...
3  mod_disco:
4    server_info:
5    -
6      modules: all
7      name: "abuse-addresses"
8      urls: ["mailto:abuse@example.net"]

Add mod_host_meta:

1  mod_host_meta:
2    bosh_service_url: "https://@HOST@/xmpp/bosh"
3    websocket_url: "wss://@HOST@/xmpp/ws"

Edit mod_mam, and change assume_mam_usage to false and default to never if it is not desirable to default to archiving messages on the server:

1  mod_mam:
2    db_type: sql
3    assume_mam_usage: never
4    default: never

Add mod_stun_disco to advertise the STUN service to clients, changing 0.0.0.0 and example.net to the server's IP and hostname respectively:

 1  mod_stun_disco:
 2    credentials_lifetime: 12h
 3    services:
 4        -
 5          host: 0.0.0.0
 6          port: 3478
 7          type: stun
 8          transport: udp
 9          restricted: false
10        -
11          host: 0.0.0.0
12          port: 3478
13          type: turn
14          transport: udp
15          restricted: true
16        -
17          host: turn.example.net
18          port: 5349
19          type: stuns
20          transport: tcp
21          restricted: false
22        -
23          host: turn.example.net
24          port: 5349
25          type: turns
26          transport: tcp
27          restricted: true

Set the host to the muc subdomain, otherwise it will attempt to use conference.example.net. Setting mam: false in default_room_options will disable server-side message archiving by default.

 1  mod_muc:
 2    host: muc.example.net
 3    access:
 4      - allow
 5    access_admin:
 6      - allow: admin
 7    access_create: muc_create
 8    access_persistent: muc_create
 9    access_mam:
10      - allow
11    default_room_options:
12      mam: false
1  mod_proxy65:
2    access: local
3    max_connections: 5
1  mod_http_upload:
2    put_url: https://@HOST@/xmpp/upload
3    docroot: /var/www/ejabberdupload
4    max_size: 1073741824
5    custom_headers:
6      "Access-Control-Allow-Origin": "https://@HOST@"
7      "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
8      "Access-Control-Allow-Headers": "Content-Type"

Create the folder for the docroot, and ensure it is owned by the ejabberd account. Change max_size (the max upload size) to whatever is preferred.

1  mod_pubsub:
2    access_createnode: pubsub_createnode
3    plugins:
4      - flat
5      - pep
6    force_node_config:
7      ## Avoid buggy clients to make their bookmarks public
8      storage:bookmarks:
9        access_model: whitelist

Start the ejabberd server!

1sudo -u ejabberd ejabberdctl start

(Depending your OS and the way you installed ejabberd, this might need to be different.)

Register an admin user admin@example.net with the password password:

1sudo -u ejabberd ejabberdctl register admin example.net password

There is a compliance tester at compliance.conversations.im to test the server.

After everything has been set up correctly, optionally change the loglevel at the root of the config.

There will be an admin page accessible at https://example.net/xmpp/admin.

It is possible to set up conversejs using mod_conversejs. It may be required to update the web server config to proxy the new endpoint (/chat below).

 1listen:
 2  -
 3    port: 5443
 4    module: ejabberd_http
 5    request_handlers:
 6      /xmpp/bosh: mod_bosh
 7      /xmpp/ws: ejabberd_http_ws
 8      /chat: mod_conversejs
 9
10modules:
11  mod_conversejs:
12    websocket_url: "ws://@HOST@/xmpp/ws"
13    bosh_service_url: "https://@HOST@/xmpp/bosh"

For each further virtualhost a new database should be created, and added to the database part of the config. e.g.:

 1host_config:
 2  example.net:
 3    sql_type: pgsql
 4    sql_server: "localhost"
 5    sql_port: 5432
 6    sql_username: "ejabberd"
 7    sql_password: "postgres_password"
 8    sql_database: "ejabberd_net"
 9    auth_method: sql
10    auth_password_format: scram
11  example.org:
12    sql_type: pgsql
13    sql_server: "localhost"
14    sql_port: 5432
15    sql_username: "ejabberd"
16    sql_password: "postgres_password"
17    sql_database: "ejabberd_org"
18    auth_method: sql
19    auth_password_format: scram

There cannot be conflicts between declarations in the config file, so if mod_muc, mod_proxy65, mod_http_upload and mod_pubsub are declared under modules at the root, they (as well as other configuration differences between virtualhosts) must be deleted and replicated for each virtualhost under append_host_config, at the root. Example as so:

 1append_host_config:
 2  example.org:
 3    modules:
 4      mod_muc:
 5        host: muc.example.org
 6        access_create: org_account
 7        access_persistent: org_account
 8        access:
 9          - allow
10        access_admin:
11          - allow: admin
12        default_room_options:
13          mam: false
14      mod_proxy65:
15        access: org_account
16        max_connections: 5
17      mod_http_upload:
18        access: org_account
19        put_url: https://@HOST@/xmpp/upload
20        docroot: /var/www/ejabberdupload
21        max_size: 1073741824
22        custom_headers:
23          "Access-Control-Allow-Origin": "https://@HOST@"
24          "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
25          "Access-Control-Allow-Headers": "Content-Type"
26      mod_pubsub:
27        access_createnode: org_account
28        plugins:
29          - flat
30          - pep
31        force_node_config:
32          ## Avoid buggy clients to make their bookmarks public
33          storage:bookmarks:
34            access_model: whitelist
35  example.net:
36    modules:
37      mod_muc:
38        hosts:
39          - muc.example.net
40        access_create: net_account
41        access_persistent: net_account
42        access:
43          - allow
44        access_admin:
45          - allow: admin
46        default_room_options:
47          mam: false
48      mod_proxy65:
49        access: net_account
50        max_connections: 5
51      mod_http_upload:
52        access: net_account
53        put_url: https://@HOST@/xmpp/upload
54        docroot: /var/www/ejabberdupload
55        max_size: 1073741824
56        custom_headers:
57          "Access-Control-Allow-Origin": "https://@HOST@"
58          "Access-Control-Allow-Methods": "GET,HEAD,PUT,OPTIONS"
59          "Access-Control-Allow-Headers": "Content-Type"
60      mod_pubsub:
61        access_createnode: net_account
62        plugins:
63          - flat
64          - pep
65        force_node_config:
66          ## Avoid buggy clients to make their bookmarks public
67          storage:bookmarks:
68            access_model: whitelist

As above, it is possible to disable access to certain services per virtualhost using ACLs, in order to e.g. prevent accounts on example.net from creating MUCs on muc.example.org.

In this case, change mod_stun_disco to this, and don't enable the listen opts for STUN/TURN. Generate an auth secret and share it with the TURN server instance.

1  mod_stun_disco:
2    secret: "auth_secret"
3    services:
4      -
5        host: turn.example.net
6        type: stun
7      -
8        host: turn.example.net
9        type: turn

Since ejabberd 26.01 there's a new module mod_invites that lets you create invite tokens, that you can pass on so others can create new accounts in a controlled way.

Let's look into different scenarios on how to configure your system to make of this new feature, the way it suits you best.

Only admin users allowed, no regular in-band registration (ibr)

1modules:
2  mod_invites: {}
3  mod_register:
4    allow_modules:
5      - mod_invites
Be sure to not enforce further restrictions on mod_register like ip_access.
1access_rules:
2  access_create_account:
3    allow: local
4#[...]
5modules:
6  mod_invites:
7    access_create_account: access_create_account
It is highly recommended to configure a landing page since this will improve user experience greatly. Otherwise you'd have to make sure a compatible client is installed already and the target system can handle XMPP URIs.

The integrated landing page is based on bootstrap4 and jQuery, due to restrictions of some Linux distributions, we can not ship libraries, that are already included by their package manangement. Depending on your installation method and your system's configuration you might therefore have to install those libraries manually.

On Debian this is as easy as

1sudo apt install libjs-jquery libjs-bootstrap4

For convenvience, there's a script included in ejabberd's source distrubtion at tools/dl_invites_page_deps.sh that you'd use like this:

1./tools/dl_invites_page_deps.sh /usr/share/javascript

Make sure you have the necessary rights to write to this directory.

If you use docker or our binary images, those might already be included.

The rest of this guide will assume we've installed those libraries to the location above.

Now configure ejabberd's http handler to serve the landing page and those libraries.

 1listen:
 2  -
 3    port: 5281
 4    module: ejabberd_http
 5    request_handlers:
 6      /xmpp/invites: mod_invites
 7      /share: mod_http_fileserver
 8# [...]
 9modules:
10  mod_http_fileserver:
11    docroot: /usr/share/javascript
12  mod_invites:
13    landing_page: https://@HOST@/xmpp/invites/{{ invite.token }}
You don't have to serve /share from ejabberd's http handler, you can also configure your preferred web-server to do so.

If you'd rather not wish to serve the included landing page, you can still configure an external service. You can either host your own or use an existing service like the one we provide at https://invites.joinjabber.org.

In this case you have to configure the landing_page parameter accordingly.

1modules:
2  mod_invites:
3    landing_page: https://invites.joinjabber.org/#{{ invite.uri|strip_protocol }}
Through an external landing page no web based registration form will be available. Accounts will then have to be created using in-band registration, which is not supported by many clients.
1sudo -u ejabberd ejabberdctl generate_invite example.net

If you want to suggest a username for this account, you can do so by

1sudo -u ejabberd ejabberdctl generate_invite_with_username sponge_bob example.net

The result will look like this (depending on your configuration):

1xmpp:example.net?register;preauth=fvR20OkCyXyjZoOSybZh4ebx	https://example.net/xmpp/invites/fvR20OkCyXyjZoOSybZh4ebx

The first part is an XMPP URI, the second a landing page (only present if configured). This is what you want to pass on.

First you want to have a dedicated access rule for mod_invite's access_create_account and there you add the abusive person's account.

1access_rules:
2  access_create_account:
3    deny:
4      - user: plankton@example.net
5    allow: local
6# [...]
7modules:
8  mod_invites:
9    access_create_account: access_create_account

Reload configuration if needed.

1sudo -u ejabberdctl reload_config

Now expire all issued invites for that account.

1sudo -u ejabberdctl expire_invite_tokens plankton example.net

You can cleanup expired and used tokens to free up space from your database. But be aware that this procedure removes all traceability ("who invited whom") and resets max_invites limits. This might affect your ability to identify abusers.

1sudo -u ejabberd ejabberdctl cleanup_expired_tokens example.net

Now it's time to spread the word! We provide a guide that shows how to use this feature for a selection of popular XMPP clients.

This is obviously not an exhaustive list and if you have questions or a really good recommendation please contact us here: servers@joinjabber.org (web chat) (xmpp)


  1. Originally adapted from: blos.sm ↩︎

  2. Virtualhosts allow more than one XMPP service to be run on one server. For example, one XMPP service with the domain example.net and another XMPP service with the domain example.org, both somewhat separated from each other as if they were run on different servers. ↩︎