Skip to content

Instantly share code, notes, and snippets.

What would you like to do?


Prosody is an XMPP server. My instance is currently hosted on a DigitalOcean VM, running in the smallest droplet avaialble, which is still far more resources than a server with just a few users needs.

At the end of these steps we will have a Prosody server which is isolated (no server-to-server comms), requires an administrator to create new user accounts, is using valid SSL certs issued by Let's Encrypt, and capable of handling file transfers up to 10MiB in size via the https upload service. This was written with CentOS 7.6.


Before beginning, make sure you have DNS entries for the following:

  • chat.domain.tld
  • conference.domain.tld
  • upload.domain.tld
  • proxy.domain.tld

Since all of these are hosted from the same server, I used an A record for chat and c-names for the others.

# apply all updates
yum -y update

# for whatever reason, RPCbind was enabled on my instance, diable it
systemctl disable rpcbind.service rpcbind.socket

# irritatingly, disable selinux since I haven't discovered rules for Prosody
sed -i 's/enforcing/disabled/' /etc/sysconfig/selinux

# reboot for service adjustments and if the kernel was updated

# install epel
yum install epel-release

# install app and firewall
yum install prosody firewalld certbot hg

# turn on the firewall
systemctl enable --now firewalld

# enable the ports we'll need.  port 80 is only used for certificates, so
# a future project is to only have it open when doing renewals
firewall-cmd --permanent --zone=public --enable-port=80/tcp

# used for the proxy
firewall-cmd --permanent --zone=public --enable-port=5000/tcp

# used for XMPP
firewall-cmd --permanent --zone=public --enable-port=5222/tcp

# used for HTTPS file uploads
firewall-cmd --permanent --zone=public --enable-port=5281/tcp

# reload to apply new firewall rules
firewall-cmd --reload

# create the certificates, todo: create a wildcard cert and move services
# to user subdomains
certbot certonly --standalone -d chat.domain.tld
certbot certonly --standalone -d conference.domain.tld
certbot certonly --standalone -d upload.domain.tld
certbot certonly --standalone -d proxy.domain.tld

# download the community modules
cd /opt && hg clone prosody-modules


  • Create the config file, /etc/prosody/prosody.cfg.lua

    I highly recommend reviewing the docs for each of the enabled modules and deciding which ones you want to enable. Docs for the core modules are here, docs for the community modules are here.

    -- set any admin users
    admins = { "you@chat.domain.tld" }
    -- the path to where we downloaded the community modules
    plugin_paths = { "/opt/prosody-modules" }
    modules_enabled = {
    -- explicitly disable server-to-server comms
    modules_disabled = {
    -- do not allow user self-registration
    allow_registration = false
    -- mandatory client-to-server encryption
    c2s_require_encryption = true
    -- s2s is disabled, but just in case we ever enable it, require encryption
    s2s_require_encryption = true
    s2s_secure_auth = true
    -- not using an external user db
    authentication = "internal_hashed"
    -- how long to keep archived messages
    archive_expires_after = "1w"
    -- important info for troubleshooting
    log = {
      info = "/var/log/prosody/prosody.log";
      error = "/var/log/prosody/prosody.err";
    -- disco allows clients to find other services on the server
    disco_items = {
      { "conference.domain.tld", "group chats" };
      { "upload.domain.tld", "file uploads" };
    -- where the server SSL certs are stored
    certificates = "/etc/pki/prosody/"
    -- this is the default SSL cert used for port 5281
    https_certificate = "/etc/prosody/certs/upload.domain.tld.crt"
    pidfile = "/run/prosody/";
    -- the primary Prosody domain
    VirtualHost "chat.domain.tld"
    -- enable multi-user rooms
    Component "conference.domain.tld" "muc"
      modules_enabled = { "mam_muc", "vcard_muc" }
    -- enable the file transfer proxy for users behind restrictive NAT
    Component "proxy.domain.tld" "proxy65"
    -- enable uploads
    Component "upload.domain.tld" "http_upload"
      -- allow files up to 10MiB to use the upload service
      http_upload_file_size_limit = 10485760

    While we're here, remove the example and localhost configs:

    rm /etc/prosody/conf.d/*
  • Import the certificates:

    prosodyctl --root cert import /etc/letsencrypt/live

    While we're on the topic of certificates, let's configure certbot for automatic renewal.

    # configure certbot to execute the prosody command for certs after updating
    cat <<EOF > /etc/sysconfig/certbot
    POST_HOOK="--post-hook 'prosodyctl --root cert import /etc/letsencrypt/live && systemctl restart prosody'"
    # enable the cert renewal service
    systemctl enable certbot-renew.timer
  • Now we can start the server:

    systemctl enable --now prosody
  • And, add users:

    prosodyctl adduser you@chat.domain.tld

Don't forget to turn on OMEMO in the client to encrypt messages between users, otherwise the Prosody admin can read them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment