So you run pleroma and you want to set up an XMPP server so your comfy friends can talk in real-time

How exactly do you go about doing that?

Well, that’s rather the point of this post. Rather in the name, eh?

Setting up your server

Assuming your infrastructure looks sorta like mine (as described in this post), we’ll need to do some setting up before we can actually install anything.

First, spin up a tiny little server to be your XMPP host. It can be very small and cute, AWS t3.micro or similar should suffice unless you run some behemoth server in which case feel free to allocate some more resources.

I advise putting ubuntu on it, since we only really care for stability and compatibility here.

As per usual, we’re going to lock the server down to our bastion host (skip this if you don’t have a bastion host)

sudo apt-get update && sudo apt-get upgrade
sudo apt-get install ufw
# OPTIONAL
# use sudo ufw allow 22 if you don't have a bastion
sudo ufw allow from BASTION_IP to any port 22
sudo ufw allow 5222
sudo ufw allow 5269

Now we want postgres to hold some data

sudo apt-get install postgresql

Setting up DNS

If you’re not running on your root server, you’ll need to point a few records at your XMPP server.

SRV _xmpp-client._tcp.MYDOMAIN -> your xmpp server ip 5222
SRV _xmpp-server._tcp.MYDOMAIN -> your xmpp server ip 5269

Installing MongooseIM

From https://www.erlang-solutions.com/resources/download.html pick “MongooseIM” and download the latest .deb file to your server.

Then you can sudo dpkg -i the_deb_file.deb to install

This will shove a load of files on your server, naturally. The stuff we care about is in /etc/mongooseim

Installing the database

This is exceptionally badly documented by the mongooseim project, so it falls to me to explain how we get the initial DB all… working.

Having installed the mongooseim user, we now need to set up the corresponding postgres user.

sudo -Hu postgres psql

postgres=# create user mongooseim with login password 'SOME_GOOD_PASSWORD';
postgres=# create database mongooseim with owner mongooseim;

cool now we just need to actually find the initial tables

sudo -Hu mongooseim psql mongooseim < /usr/lib/mongooseim/lib/mongooseim-?.?.?/priv/pg.sql

If for whatever reason the file isn’t there, find it with find / -iname "pg.sql" -type f

Configuration

Configuration of mongooseim is done in erlang. It’s like the trenches of the somme in there so good luck.

The standard config isn’t going to be doing much for you, so just take my config and alter it

VERY IMPORTANT NOTE - fullcert.pem MUST be a concatenated file of both public and private key. If you’re using letsencrypt make sure you cat the two pem files together.

If you want to be running multi-user rooms you’ll want your SSL cert to also cover muc.mydomain, but i don’t do that so don’t come asking me for how it works.

%% 1: Critical
%% 2: Error
%% 3: Warning
%% 4: Info
%% 5: Debug
%%
{loglevel, 5}.

%%%.   ================
%%%'   SERVED HOSTNAMES

{hosts, ["YOUR_FEDIVERSE_SERVER"] }.

%%%.   ===============
%%%'   LISTENING PORTS
{listen,
 [
  %% MongooseIM HTTP API it's important to start it on localhost
  %% or some private interface only (not accessible from the outside)
  %% At least start it on different port which will be hidden behind firewall
  { {8088, "127.0.0.1"} , ejabberd_cowboy, [
      {num_acceptors, 10},
      {transport_options, [{max_connections, 1024}]},
      {modules, [
          {"localhost", "/api", mongoose_api_admin, []}
      ]}
  ]},

  { 5222, ejabberd_c2s, [
      {certfile, "/etc/mongooseim/ssl/fullcert.pem"}, starttls,
			{access, c2s},
			{shaper, c2s_shaper},
			{max_stanza_size, 65536},
			{protocol_options, ["no_sslv3"]}      
  ]},


  { 5269, ejabberd_s2s_in, [
			   {shaper, s2s_shaper},
			   {max_stanza_size, 131072},
			   {protocol_options, ["no_sslv3"]}

	]},

  { 8888, ejabberd_service, [
                {access, all},
                {shaper_rule, fast},
                {ip, {127, 0, 0, 1}},
                {password, "secret"}
  ]}
 ]}.

{s2s_use_starttls, required}.
{s2s_certfile, "/etc/mongooseim/ssl/fullcert.pem"}.

{s2s_default_policy, allow }.

{outgoing_s2s_port, 5269 }.

{sm_backend, {mnesia, []} }.

{sasl_mechanisms, [cyrsasl_plain]}.

{auth_method, http}.
{auth_opts, [
	{password_format, plain}   
]}.


{outgoing_pools, [
  {http, global, auth, [{workers, 50}], [{server, "https://YOUR_FEDIVERSE_SERVER,.com"}]},
  {rdbms, global, default, [{workers, 10}], [{server, {pgsql, "127.0.0.1", 5432, "mongooseim", "mongooseim", "YOUR_GOOD_PASSWORD"}}]}
]}.

%% == Extra options ==
%%
%% If you use PostgreSQL, have a large database, and need a
%% faster but inexact replacement for "select count(*) from users"
%%
%%{pgsql_users_number_estimate, true}.
%%
%% rdbms_server_type specifies what database is used over the RDBMS layer
%% Can take values mssql, pgsql, mysql
%% In some cases (for example for MAM with pgsql) it is required to set proper value.
%%
{rdbms_server_type, pgsql}.

%%%.   ===============
%%%'   TRAFFIC SHAPERS

%%
%% The "normal" shaper limits traffic speed to 1000 B/s
%%
{shaper, normal, {maxrate, 1000}}.

%%
%% The "fast" shaper limits traffic speed to 50000 B/s
%%
{shaper, fast, {maxrate, 50000}}.

%%
%% This option specifies the maximum number of elements in the queue
%% of the FSM. Refer to the documentation for details.
%%
{max_fsm_queue, 1000}.

%%%.   ====================
%%%'   ACCESS CONTROL LISTS

%%
%% The 'admin' ACL grants administrative privileges to XMPP accounts.
%% You can put here as many accounts as you want.
%%
{acl, admin, {user, "myusername", "YOUR_FEDIVERSE_SERVER"}}.


%%
%% Local users: don't modify this line.
%%
{acl, local, {user_regexp, ""}}.

%%%.   ============
%%%'   ACCESS RULES

%% Maximum number of simultaneous sessions allowed for a single user:
{access, max_user_sessions, [{10, all}]}.

%% Maximum number of offline messages that users can have:
{access, max_user_offline_messages, [{5000, admin}, {100, all}]}.

%% This rule allows access only for local users:
{access, local, [{allow, local}]}.

%% Only non-blocked users can use c2s connections:
{access, c2s, [{deny, blocked},
	       {allow, all}]}.

%% For C2S connections, all users except admins use the "normal" shaper
{access, c2s_shaper, [{none, admin},
		      {normal, all}]}.

%% All S2S connections use the "fast" shaper
{access, s2s_shaper, [{fast, all}]}.

%% Admins of this server are also admins of the MUC service:
{access, muc_admin, [{allow, admin}]}.

%% Only accounts of the local ejabberd server can create rooms:
{access, muc_create, [{allow, local}]}.

%% All users are allowed to use the MUC service:
{access, muc, [{allow, all}]}.

%% In-band registration allows registration of any possible username.
%% To disable in-band registration, replace 'allow' with 'deny'.
{access, register, [{allow, all}]}.

%% By default the frequency of account registrations from the same IP
%% is limited to 1 account every 10 minutes. To disable, specify: infinity
{registration_timeout, infinity}.

%% Default settings for MAM.
%% To set non-standard value, replace 'default' with 'allow' or 'deny'.
%% Only user can access his/her archive by default.
%% An online user can read room's archive by default.
%% Only an owner can change settings and purge messages by default.
%% Empty list (i.e. `[]`) means `[{deny, all}]`.
{access, mam_set_prefs, [{default, all}]}.
{access, mam_get_prefs, [{default, all}]}.
{access, mam_lookup_messages, [{default, all}]}.
{access, mam_purge_single_message, [{default, all}]}.
{access, mam_purge_multiple_messages, [{default, all}]}.

%% 1 command of the specified type per second.
{shaper, mam_shaper, {maxrate, 1}}.
%% This shaper is primeraly for Mnesia overload protection during stress testing.
%% The limit is 1000 operations of each type per second.
{shaper, mam_global_shaper, {maxrate, 1000}}.

{access, mam_set_prefs_shaper, [{mam_shaper, all}]}.
{access, mam_get_prefs_shaper, [{mam_shaper, all}]}.
{access, mam_lookup_messages_shaper, [{mam_shaper, all}]}.
{access, mam_purge_single_message_shaper, [{mam_shaper, all}]}.
{access, mam_purge_multiple_messages_shaper, [{mam_shaper, all}]}.

{access, mam_set_prefs_global_shaper, [{mam_global_shaper, all}]}.
{access, mam_get_prefs_global_shaper, [{mam_global_shaper, all}]}.
{access, mam_lookup_messages_global_shaper, [{mam_global_shaper, all}]}.
{access, mam_purge_single_message_global_shaper, [{mam_global_shaper, all}]}.
{access, mam_purge_multiple_messages_global_shaper, [{mam_global_shaper, all}]}.

{language, "en"}.

{all_metrics_are_global, false }.

{services,
    [
        {service_admin_extra, [{submods, [node, accounts, sessions, vcard,
                                          roster, last, private, stanza, stats]}]},
	{service_cache, []}
    ]
}.

{modules,
 [
  {mod_adhoc, []},
  {mod_disco, [{users_can_see_hidden_services, false}]},
  {mod_commands, []},
  {mod_muc_light_commands, []},
  {mod_last, []},
  {mod_stream_management, [
                          ]},
  {mod_offline, [{access_max_user_messages, max_user_offline_messages}, {backend, rdbms}]},
  {mod_privacy, [{backend, rdbms}]},
  {mod_blocking, []},
  {mod_muc_light, [
    {host, "muclight.@HOST@"},
    {backend, rdbms},
    {access_create, none},
    {all_can_invite, true},
    {all_can_configure, false},
    {blocking, false},
    {max_occupants, 500},
    {rooms_per_page, 50},
    {rooms_in_rosters, true}
   ]},
  {mod_event_pusher, [
    {backends, [
        {push, [
            {backend, mnesia},
            {wpool, [{workers, 200}]},
            {plugin_module, mod_event_pusher_push_plugin_defaults}
        ]}
    ]}
  ]},
  {mod_private, [{backend, rdbms}]},
  {mod_inbox, [
    {backend, rdbms},
    {reset_markers, [displayed, received, acknowledged]},
    {aff_changes, true},
    {remove_on_kicked, true},
    {groupchat, [muclight]}
  ]},
  {mod_roster, [{backend, rdbms}, {versioning, true}]},
  {mod_sic, []},
  {mod_pubsub, [
    {access_createnode, pubsub_createnode},
    {ignore_pep_from_offline, false},
    {last_item_cache, mnesia},
    {max_items_node, 1000},
    {plugins, [<<"flat">>, <<"pep">>]},
    {backend, rdbms}
  ]},
  {mod_vcard, [
	{host, "vjud.@HOST@"}
  ]},
  {mod_bosh, []},
  {mod_carboncopy, []},
  {mod_caps, []},
  {mod_mam_meta, [
    {backend, rdbms},
    {async_writer, true },
    {archive_groupchats, true },
    {user_prefs_store, mnesia},
    {rdbms_message_format, simple},
    {pm, [
      {archive_groupchats, true}
    ]},
    {muc, [
      {host, "muclight.@HOST@"}
    ]}
  ]},
  {mod_csi, [{buffer_max, 40}]}
]}.

%%%.
%%%'

%%% $Id$

%%% Local Variables:
%%% mode: erlang
%%% End:
%%% vim: set filetype=erlang tabstop=8 foldmarker=%%%',%%%. foldmethod=marker:
%%%.

Starting the server

Ok cool i hope your PTSD isn’t that bad right now but i wouldn’t blame you if you had to go to therapy after seeing that.

start by running mongooseimctl foreground and making sure it starts all ok.

if it does, feel free to just mongooseimctl start it and you’ll be good to go.