Installing Prosody: Instant Message Chat server for Raspberry Pi / Ubuntu


At the end of 2013, I wrote a series of tutorials explaining how to run an email server at home on a raspberry pi, which turned out to be very popular.

I'm now aiming to do the same for instant messaging. This tutorial will:

  • Introduce the Extensible Messaging and Presence Protocol (XMPP), formerly known as "Jabber"
  • Discuss some of the pros and cons of XMPP vs some other proprietary protocols
  • Give instructions for the installation and configuration of Prosody, a free and open source XMPP server.
  • Discuss various important XMPP Extension Protocols (XEPs) and how to enable them
  • Cover transport layer encryption (i.e. client-server and server-server TLS) and end-to-end encryption for clients
  • Recommend client software for different platforms

Along the way, I'll expose the underlying protocol to give you the tools you need to debug the problem yourself, instead of relying on obscured error messages from GUI applications.

This is part 1 of a 2 part tutorial. Part 2 can be found here.

All About XMPP

XMPP is an open protocol that chat clients and servers use to communicate to each other, the equivalent of SMTP (the open protocol for email) but for instant messaging.

If you don't care about the background or already know all about XMPP, you can skip to Prosody installation.

History and adoption

The Jabber protocol was developed from the start as an open protocol, along with a free and open source server implementation called "jabberd". Unfortunately, it has been far less successful than SMTP.

The first version of the XMPP protocol was released in 2000, 18 years later than the first release of SMTP in 1982. In my opinion its late release was one of the major reasons for its low adoption rate: by the time it came on the scene it was already competing with proprietary protocols like MSN messenger (released 1999), and the internet was a far less academic and more commercially driven place. Interoperability of chat clients is also less mission critical to businesses than it is for email, which reduces the pressure for everyone to use a standard protocol.

Unfortunately, this has led to a situation where a typical mobile phone user may have three or four chat clients installed, all using different proprietary services with no inter-operability. Think about how absurd this is compared to the situation we have with email: it's the equivalent of Google offering you a free Gmail account and saying you can only use it to email other Gmail users!

Ports and communication model

Like SMTP, it is based on a decentralised client-server model: anyone can run their own server, clients communicate with the server and the servers pass messages to each other.

Client to server communication uses port 5222.
Server to server communication uses port 5269.

Unlike SMTP, Internet Service Providers (ISPs) are much less likely to block outgoing connections on the well-known ports for XMPP than they are for SMTP: email spam on port 25 is much more common than XMPP spam, partly due to its popularity. This is good news for you, because it makes it much easier to run a server at home.

The actual data transfer for XMPP messages is achieved using XML (that's the plain text format where everything is nested in loads of < and >).

A typical message sent in XML looks something like this:

<message from=''
    <body>Art thou not Romeo, and a Montague?</body>

For more examples, see the XMPP wiki.

Pros and Cons

XMPP has some strengths, but it also has weaknesses. It doesn't help anyone to pretend it's perfect, so here's what I hope is a balanced discussion of both:


  • Many free providers: you can choose to run your own XMPP server, which gives you total control. However, you don't have to - there are many free providers (list), some of which are run by well known free software organisations like KDE's server at
  • XMPP is Decentralised: unlike proprietary services like WhatsApp and Blackberry Messenger, there isn't one single provider in control of the whole network. This ensures that XMPP continues to serve the needs of its users, since providers cannot introduce anti-features without users switching to another service.
  • Large selection Client and Server software: every platform I can think of has at least one XMPP client (Android has at least 9, and those are just the ones in the F-Droid store - there are many other proprietary clients), and there is a healthy selection of server software to choose from too. You really can send messages from every device, without arbitrary restrictions imposed for non-technical reasons (e.g. WhatsApp's decision not to allow installation on tablets).
  • Stability of service: XMPP has endured the boom and bust of many proprietary services, and is likely to endure whatever the next decade throws at it. No company can decide to end XMPP, or make it paid, or start including adverts in it. When Blackberries were popular, it seemed like "everyone" used Blackberry Messenger, but then they went out of fashion and everyone switched to a different platform, with new accounts etc... why bother with all this when you can use one account that isn't tied to a particular platform or service?
  • Flexibility: due to its extensible nature, you can use the XEPs you want to add the features you want.


  • Not all features are supported everywhere: this is the downside of having an extensible protocol - for some features to work, your contact has to have a client/server that supports the XEPs you are trying to use. This is most noticeable in photo sharing; there are many different ways of sending images, as we will discuss later. However, the XMPP standards foundation does publish some minimum requirements for compliance in an effort to ensure the most widely used features are supported by most clients (2010, 2012).
  • Ensuring message delivery is problematic: there is no mechanism for ensuring messages have been delivered built in to XMPP. This is probably due in part to the fact that when it was devised, people using it were on desktop computers with reasonably steady internet connections. Either the person was online and you talked to them, or they were offline and you didn't. Now that more and more clients are on smartphones, internet connectivity is less steady and it is easy to miss messages if your signal comes and goes. Some proprietary IM services like Facebook chat apparently echo every message back to the server to prove delivery (how wasteful!); XMPP has solved the problem with various XEPs, but they are not part of the basic standard. The good news here is that message delivery is mostly a client-server problem, so it can be dealt with in your prosody configuration.
  • Not popular: it's unlikely that your friends already use XMPP or even know what it is. A good rule of thumb is that the more someone knows about Free Software, the more likely they are to use it. Whether you can persuade them to use it or not is down to you.
  • Volatile relationship with for-profit organisations: Google and Facebook both supported XMPP, once upon a time, but they have since dropped (or are dropping) that support. In fact, Google Hangouts and Facebook Messenger are both based on XMPP. Recommending XMPP because "company X provides an XMPP gateway to their proprietary service" is a dangerous game - it seems like this is only something that companies do when they are trying to attract users. Once they have enough users, the financial incentive always favours the vendor-lockin approach, and XMPP support is dumped or not updated.
  • Development can be slow: while proprietary messaging services have a strong incentive to innovate and add new features to attract users, the inter-operability problem with XMPP clients and servers means new features are added slowly, often only when a few popular projects get organised and decide to support it together. The Android client Conversations is an exception here, continually pushing the boundaries by supporting new XEPs that benefit mobile users - more on that later.

Prosody vs other servers

Before trying prosody, I installed and tested various different XMPP servers including Ejabberd (Erlang Jabber Daemon) and Jabberd2 as well as researching and discounting various others due to their feature sets. There's a useful table comparing XEP support of various XMPP servers on Wikipedia.

Initially I was prejudiced against prosody because it is written in a scripting language called Lua, and I thought the code quality was likely to be lower than the other options. I was wrong! I'm not going to go into it in great detail, but take it from me: configuring prosody is a dream compared to the rest. Here are a few short points about my choice of Prosody:

Both Ejabberd and Jabberd2 have very complicated configuration files with fairly obtuse syntax, and are not well suited to small installations for beginners.

Jabberd2 is a project that grew out of the original open source Jabberd server released with the original installation. I couldn't even get it to work on Ubuntu - not sure if that was a packaging thing or my own stupidity, but I gave it half a day and then gave up.

Ejabberd is apparently very good for large installations because it scales well and integrates with other services like Asterisk for VOIP calls etc. This is what most of the large Jabber nodes run as server software. I got Ejabberd up and running just fine, but it is far more difficult to understand than prosody - the whole thing runs on an Erlang (the language that Ejabberd is written in) Virtual Machine node, which is a separate process to the Ejabberd daemon and has its own controls. See what I mean? Having said that, the Ejabberd documentation is excellent, and I will probably experiment with it again in future - it's just not the most friendly server for beginners.

Installing Prosody

There are a few different branches of prosody. The current stable branch is 0.9 and the development branch is 0.10. If you install straight from the repository in Raspbian or Ubuntu you'll get 0.9, but some of the modules required to support newer XEPs that are important for mobile require version 0.10.

So, the first thing we need to do is add the Prosody repository to get v0.10. The repository contains packages for many different release codenames of Debian and Ubuntu. You can find out which one you need by running:

lsb_release -sc

For Ubuntu 14.04, this is trusty; for 16.04 it's xenial. The old stable raspbian/debian is wheezy and the new is jessie. The next step is to use that code name to add the right repository, e.g.

sudo add-apt-repository 'deb xenial main'

It's also necessary to add the prosody developers' key to your system so that package verification succeeds:

wget -O- | sudo apt-key add -

Now update the packages list:

sudo apt-get update

You should now see multiple versions of prosody available for installation:

sam@samhobbs:/etc$ apt-cache search prosody
prosody - Lightweight Jabber/XMPP server
prosody-0.10 - Lightweight Jabber/XMPP server
prosody-0.9 - Lightweight Jabber/XMPP server
prosody-trunk - Lightweight Jabber/XMPP server

If you don't specify which one you want, you'll get version 0.9, so use this command to install version 0.10:

sudo apt-get install prosody-0.10

Initial Testing

Verify that Prosody is running

The default prosody configuration should "just work" - the server should be running, but we haven't added any users yet. Verify it's running using systemctl:

sudo systemctl status prosody

Prosody also comes with its own utility for controlling the XMPP server, called prosodyctl (similar to Apache's apachectl). You can also check the status of prosody using this tool like so:

sam@server:/etc/prosody$ sudo prosodyctl status
Prosody is running with PID 30482

Make a simple client to server connection

As mentioned previously, XMPP servers listen for client to server connections on port 5222 and server to server connections on port 5269. If you used the mail server tutorial I wrote, you'll remember we tested the server by sending commands using telnet. I found some useful XMPP tests here.

Here's a simple connection request using XML, which requests a client-to-server (C2S) connection for the hostname 'localhost':

<?xml version="1.0"?>
<stream:stream xmlns:stream="" version="1.0" xmlns="jabber:client" to="localhost" xml:lang="en" xmlns:xml="">

Open a C2S connection to prosody using telnet:

telnet localhost 5222

Now copy and paste the query above into telnet (to paste in a terminal is normally CTRL+SHIFT+V), and you should get a response something like this - I've indented it a bit to make it easier to read:

<?xml version='1.0'?>
<stream:stream xmlns:stream='' xml:lang='en' from='localhost' id='9ea2578f-c696-49fd-bc70-f51d05a2e444' version='1.0' xmlns='jabber:client'>
    <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>

Here the server is declaring that all connections must use STARTTLS, a method of upgrading plaintext connections to encrypted connections using SSL/TLS - more on that later!

The default prosody config file is set up to serve requests on the local server (localhost) only. If you change the to="localhost" part of the data you send to something like to="", you will get a different response from the server similar to this:

<?xml version='1.0'?>
<stream:stream xmlns:stream='' xml:lang='en' from='' id='2f42fd30-034e-4c1e-9d0e-c2bef7da0e27' version='1.0' xmlns='jabber:client'>
        <host-unknown xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
        <text xmlns='urn:ietf:params:xml:ns:xmpp-streams'>This server does not serve</text>

One of the things we will do in the course of this tutorial is to add a virutualhost (again, terminology that will be familiar to apache users) to the prosody configuration, allowing it to have multiple configurations for different domains.

Upgrade to a TLS connection using STARTTLS

The server told us that we had to upgrade to an encrypted connection using STARTTLS to use the server. You can't test a STARTTLS connection with telnet, but you can use openssl s_client if you use the right options:

openssl s_client -connect localhost:5222 -quiet -starttls xmpp

At the moment your server is using a self-signed key & certificate that were automatically generated when prosody was installed (/etc/prosody/certs/localhost.key and /etc/prosody/certs/localhost.crt) so you will see an error about the server certificate being self-signed, like this:

depth=0 O = lan, OU = server, CN = localhost, emailAddress = root@server.lan
verify error:num=18:self signed certificate
verify return:1
depth=0 O = lan, OU = server, CN = localhost, emailAddress = root@server.lan
verify return:1

If we had omitted the -quiet option, openssl would have printed the whole certificate to stdout.

At this point, if you copy and paste the first request to localhost we used earlier, you will get a different response:

<?xml version='1.0'?>
<stream:stream xmlns:stream='' xml:lang='en' from='localhost' id='269e52eb-d113-4b28-a4fb-43a123098d60' version='1.0' xmlns='jabber:client'>

Here the server is listing some SASL authentication mechanisms that are available over TLS connections, which were not previously advertised over the plain text telnet connection (they wouldn't be accepted on the plain text connection either).

PLAIN is a mechanism where the password is sent in plain text, and the two SCRAM options are Salted Challenge Response Authentication Mechanisms, designed to prevent your password being sent in plain text or stored in plain text on the server. Seeing as we are using TLS, it doesn't matter if we send the password in plain text because the session itself is encrypted.

Create a test user

Before we can test SASL authentication, we need to create some users.

Remember prosodyctl? It's also a really useful tool for adding accounts to prososdy, which are separate from system logins. To create an account, all you need to do is:

sudo prosodyctl adduser

The command above will prompt you to provide a password for the new account. Alternatively, you can do it in one hit:

sudo prosodyctl register user password

For the purposes of testing, let's create a test user (we'll delete it later when we're done testing):

sudo prosodyctl register test localhost test1234

Username and password combinations are base64 encoded before submission during authentication. To base64 encode a username and password, you can use this command:

printf '\0%s\0%s' 'username' 'password' | openssl base64

For our test user, this is:

printf '\0%s\0%s' 'test' 'test1234' | openssl base64

Important: never post a real base64 encoded username and password online during debugging - it's trivial to convert it back to the human readable form.

Test SASL authentication

Now we can attempt to log in. Make a connection:

openssl s_client -connect localhost:5222 -quiet -starttls xmpp

Send the stream request:

<stream:stream xmlns:stream="" version="1.0" xmlns="jabber:client" to="localhost" xml:lang="en" xmlns:xml="">

The server sends the same response as earlier, and we can authenticate using the base64 encoded username and password:

<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">AHRlc3QAdGVzdDEyMzQ=</auth>

The server should respond indicating that the authentication was successful:

<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>

If that worked, you can delete the test user:

sudo prosodyctl deluser test@localhost

Configuring Prososdy

The prosody configuration is contained in the /etc/prosody directory and the main configuration file is /etc/prosody/prosody.cfg.lua.

A double dash (--) is a comment - everything after it is ignored.

Adding a VirtualHost for your domain

At the moment, the server only handles requests for local users (user@localhost), we need to add one or more virtualhosts for the domains you want to use. Scroll down to the Virtual Hosts part of the file, and add your domain:

VirtualHost ""
    enabled = true

Transport Layer Security (TLS)

Transport layer security is important to ensure that your passwords and private communications can not be intercepted between the client and server, and also between your server and other servers.

It used to be the case that many public XMPP servers didn't support server to server encryption, but the situation has improved greatly since the Snowden revelations prompted a campaign to support encryption across the whole network. Here's a quotation from the manifesto:

We, as operators of federated services and developers of software
programs that use the XMPP standard for instant messaging and
real-time communication, commit to establishing ubiquitous encryption
over our network on May 19, 2014.
This commitment to encrypted connections is only the first step
toward more secure communication using XMPP, and does not obviate
the need for technologies supporting end-to-end encryption (such as
Off-the-Record Messaging or OTR), strong authentication, channel
binding, secure DNS, server identity checking, and secure service
delegation. Although we have worked to implement and deploy such
technologies and will continue to do so, we believe that encrypting
the traffic on the XMPP network is a necessary precondition to
offering further security improvements.

Choice of TLS certificate

When prosody is installed, a self-signed certificate is automatically created. While this will work for testing purposes, you will get certificate errors when you try to connect to the server because the certificate has not been signed by a trusted certificate authority.

You have three options here:

  • Use the default certificates and get errors/warnings when you connect. Not recommended!
  • If you don't want to pay for a certificate authority to sign your certificate and every client connecting to the server is under your control (your phone, your laptop etc.) then you can install the CAcert root certificate on each client device and get a CAcert to sign your certificate for free as described in my CAcert signing tutorial. Even then, you might have problems because other servers may not have the CAcert root certificate in their trusted certificate store, so sending messages from your server to other servers may fail.
  • If you can pay for a TLS cert, generate a Certificate Signing Request (CSR) using the same method as the CAcert tutorial, and send it to a commercial certificate authority to get it signed. Afterwards, generate a certificate bundle according to the instructions in this tutorial I wrote to explain the differences between CAcert and commercial CAs. This is the recommended option.

Installing TLS certificates

Whichever option you go for, you should end up with a private key ( and either a certificate bundle containing your certificate and the CA's certificate ( or if you used CAcert just a certificate file ( For the rest of the tutorial I will assume you have a certificate bundle.

These files should be stored in the /etc/prosody/certs directory. Assuming the files are in the current directory, you can move or copy the cert and keyfile using these commands:

sudo mv /etc/prosody/certs/
sudo mv /etc/prosody/certs/

Since the key files are sensitive information, we need to be careful which processes can read them. Prosody needs read access to both the certificate and the key file, but doesn't need write access to either. Other processes can read the certificates (because it's public info) but shouldn't be able to read the key files. We can achieve this by making the files owned by root with group prosody, and set permissions of 640 for the key and 644 for the certs.

sudo sh -c 'chown root:prosody /etc/prosody/certs/*'
sudo sh -c 'chmod 640 /etc/prosody/certs/*.key'
sudo sh -c 'chmod 644 /etc/prosody/certs/*.crt'
sudo sh -c 'chmod 644 /etc/prosody/certs/*.ca-bundle'

We're using the sudo sh -c 'command' technique to run a specific command in a shell as root, because the /etc/prosody/certs permissions don't allow normal users to view the contents of the directory (it's missing the execute bit), so using plain old sudo chown... would give an error like chown: cannot access ‘/etc/prosody/certs/*’: No such file or directory.

Permissions should now look something like this:

sam@samhobbs:/etc/prosody$ sudo ls -l certs
total 40
-rw-r--r-- 1 root prosody 6367 Mar 19  2015
-rw-r--r-- 1 root prosody 2264 Mar 19  2015
-rw-r----- 1 root prosody 3247 Mar 19  2015
-rw-r--r-- 1 root prosody 1318 Mar 20  2015 localhost.crt
-rw-r----- 1 root prosody 1704 Mar 20  2015 localhost.key
-rw-r--r-- 1 root prosody 6006 Mar  7  2015
-rw-r--r-- 1 root prosody 1903 Mar  7  2015
-rw-r----- 1 root prosody 1679 Mar  7  2015

Tell Prosody to use the new cert

We need to instruct prosody to use these certificates in the configuration file at /etc/prosody/prosody.cfg.lua. Find the block beginning ssl = { and edit it to point to your certificates like this:

ssl = {
        key = "/etc/prosody/certs/";
        certificate = "/etc/prosody/certs/";

This sets the default certificate and keyfile that will be sent to clients when they connect with TLS. Since clients will be connecting with STARTTLS (a session that starts out as plain text before encryption is negotiated), the server can choose which certificate to send depending on which hostname was requested in the plaintext part of the negotiation.

This means that, unlike many other daemons like apache2 where there is no negotiation stage when the client connects (straight up TLS, not STARTTLS), you can have multiple virtualhosts that all serve different TLS certificates.

If you are configuring more than one domain, the place to override the default TLS cert is in the virtualhost configuration, like this:

VirtualHost ""
        enabled = true
        ssl = {
                key = "/etc/prosody/certs/";
                certificate = "/etc/prosody/certs/";

Now restart prosody, and make sure it's running:

sudo prosodyctl restart
sudo prosodyctl status

Testing the new TLS certs

Connect using openssl again, but this time specify the server name you want using -servername If the certificate is trusted and the name on the cert matches the servername you sent in the request, you should get output like this:

$ openssl s_client -connect localhost:5222 -quiet -starttls xmpp -servername
depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root
verify return:1
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = COMODO RSA Domain Validation Secure Server CA
verify return:1      
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN =
verify return:1

Connecting with XMPP clients

There are a number of things we need to do before we can connect with a proper XMPP client.

Create some real users and add yourself as an admin

Create a username for yourself, and possibly another username if you want to test sending messages to yourself using two different clients:

sudo prosodyctl adduser

In the prosody config file, find the admins block and change it so that your username is added as an admin for the server e.g.:

admins = { "" }

Being an administrator allows you to do certain things from a XMPP client like create new chat rooms, set a message of the day etc, if your XMPP client supports this.

DNS records

The only record you really need for DNS to work is a DNS A record for whichever hostname your Jabber ID is using (i.e. for, which maps that domain name to the WAN IP address of your server.

If you already have a DNS A record for pointing to a server at a different IP address, you can use SRV records (XMPP's equivalent of MX records for SMTP) to tell clients and servers to use a subdomain for XMPP. SRV records can be created with your DNS provider. You may want to create SRV records even if you have a very simple setup, because it's good practice.

Here are some example SRV records that tell clients and servers wanting to connect to the XMPP server responsible for addresses to the subdomain In this case, the subdomain would need its own DNS A record. 18000 IN SRV 0 5 5222 18000 IN SRV 0 5 5269 

To test retrieving these records, we can use a commandline tool called dig, which is in the dnsutils package. First, install dig:

sudo apt-get update
sudo apt-get install dnsutils

To retrieve a DNS A record, use:


If you want a more compact answer from dig that removes all of the extra information returned in a query, then use:

dig +noall +answer

The +noall option turns off all of the sections in the query result, and the +answer option turns the answer back on.

You should see something like this:

$ dig +noall +answer         32195   IN      A

To retrieve a SRV record, use:

dig +noall +answer srv

You should see something like this:

$ dig +noall +answer srv 1369 IN SRV   10 5 5222

Enabling WAN access

So far, we have been connecting locally to test the server. Before youcan start connecting some real client applications, you need to forward some ports on your router to make the server accessible from the internet.

Navigate to your router's control panel (usually at or If you don't know your router's IP address, use the route command:

$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface         UG    0      0        0 eno1   U     0      0        0 eno1

The G in flags tells us that the first line is the gateway. Add the following port forwards to your server:

  • Port 5222 for client to server connections
  • Port 5269 for server to server connections (you can skip this if you don't want to be able to send messages to users on different servers).

Install an XMPP client for your chosen platform

Here are some suggestions for clients you might like to use, by platform. All of them are open source software:

  • Android: Conversations (available on F-Droid for free) or on the Play store
  • Desktop Linux: KDE Telepathy, Gajim (GTK) or Pidgin
  • Sailfish OS (mobile linux OS) has an XMPP client built into the OS
  • Windows: Pidgin
  • iOS: Monal
  • OS X: Adium, which uses the same backend as Pidgin (libpurple)

Next steps

You should now have a functional, albeit basic, XMPP server. Feel free to connect to the server using the logins you created and send some messages between different accounts. However, we're not done yet!

In the next part, I'll discuss some of the many modules available for Prosody that can be used to extend its functionality and provide support for the many XEPs (protocol extensions), including group chats and file sharing. Continue to part 2.


Add new comment