Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.allthingslinux.org/llms.txt

Use this file to discover all available pages before exploring further.

Prosody is configured through a single Lua file (prosody.cfg.lua) that uses environment variable substitution at container startup, giving you full control over domains, security, storage, and components without editing Lua directly.

Configuration file structure

The main configuration lives at apps/prosody/config/prosody.cfg.lua. It is a monolithic Lua file organised into clearly labelled sections. Environment variables are read via os.getenv() with sensible fallback defaults.

Section overview

SectionPurpose
Plugin pathsCommunity and custom module directories
modules_enabledAll globally loaded modules, grouped by function
modules_disabledExplicitly disabled auto-loaded modules
Core server settingsPID file, user/group, admin JIDs
Data storageSQLite/PostgreSQL backend, per-store assignments
Message archiving (MAM)Retention, compression, query limits
NetworkingPorts, interfaces, IPv6, backend tuning
HTTP servicesBOSH, WebSocket, file upload, CORS, security headers
TURN/STUNExternal TURN server for audio/video calls
LoggingConsole and file log levels, OpenMetrics
SecurityRate limits, registration throttling, anti-spam
TLS/SSLProtocol version, ciphers, certificate paths
AuthenticationSASL mechanisms, hashed storage, blocked usernames
OAuth2Bearer token generation for Portal integration
Push notificationsCloud notify settings for mobile clients
VirtualHostPrimary domain with per-host modules and SSL
ComponentsMUC, file upload, proxy65, PubSub, bridge
Contact infoAdmin contacts, server info, account cleanup

Environment variables

All Prosody settings can be tuned via environment variables defined in .env. The config reads them with os.getenv() and falls back to defaults when unset.

Core XMPP variables

VariableDescriptionDefault
XMPP_DOMAINPrimary XMPP domain (sets PROSODY_DOMAIN in compose)atl.chat
PROSODY_ADMIN_EMAILAdmin email for contact infoadmin@allthingslinux.org
PROSODY_ADMIN_JIDAdmin JID for prosodyctl and MUC ownershipadmin@<domain>
PROSODY_ALLOW_REGISTRATIONEnable in-band registration (true/false)false
PROSODY_LOG_LEVELMinimum log level (debug, info, warn, error)info

Port variables

VariableDescriptionDefault
PROSODY_C2S_PORTClient-to-server (STARTTLS)5222
PROSODY_S2S_PORTServer-to-server (federation)5269
PROSODY_HTTP_PORTHTTP (BOSH/WebSocket)5280
PROSODY_HTTPS_PORTHTTPS (via nginx proxy)5281
PROSODY_C2S_DIRECT_TLS_PORTClient-to-server direct TLS5223
PROSODY_S2S_DIRECT_TLS_PORTServer-to-server direct TLS5270
PROSODY_PROXY65_PORTSOCKS5 file transfer proxy5000

Security variables

VariableDescriptionDefault
PROSODY_C2S_REQUIRE_ENCRYPTIONRequire TLS for client connectionstrue
PROSODY_S2S_REQUIRE_ENCRYPTIONRequire TLS for server federationtrue
PROSODY_S2S_SECURE_AUTHRequire certificate-based s2s authtrue
PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTHAllow plaintext passwords without TLSfalse
PROSODY_TLS_CHANNEL_BINDINGEnable TLS channel binding for SASLtrue
PROSODY_OAUTH2_REGISTRATION_KEYOAuth2 dynamic client registration key(must be set)
PROSODY_MAX_CONNECTIONS_PER_IPMax concurrent connections per IP5
PROSODY_REGISTRATION_THROTTLE_MAXMax registrations per throttle period3
PROSODY_REGISTRATION_THROTTLE_PERIODThrottle window for registration rate limiting (seconds)3600

Database variables

VariableDescriptionDefault
PROSODY_DB_PORTDatabase port5432
PROSODY_DB_NAMEDatabase nameprosody
PROSODY_DB_USERDatabase userprosody
PROSODY_DB_PASSWORDDatabase password(change before production)

MAM (message archiving) variables

VariableDescriptionDefault
PROSODY_ARCHIVE_EXPIRES_AFTERArchive retention period1y
PROSODY_ARCHIVE_POLICYArchive all conversations by defaulttrue
PROSODY_ARCHIVE_COMPRESSIONCompress archived messagestrue
PROSODY_ARCHIVE_MAX_QUERY_RESULTSMax results per MAM query250

MUC variables

VariableDescriptionDefault
PROSODY_MUC_DEFAULT_PUBLICNew rooms are public by defaulttrue
PROSODY_MUC_DEFAULT_PERSISTENTNew rooms persist by defaulttrue
PROSODY_MUC_LOCKINGLock rooms until configuredfalse
PROSODY_MUC_LOG_BY_DEFAULTArchive MUC messages by defaulttrue
PROSODY_MUC_LOG_EXPIRES_AFTERMUC archive retention1y
PROSODY_RESTRICT_ROOM_CREATIONRestrict who can create roomsfalse

TURN/STUN variables

VariableDescriptionDefault
TURN_SECRETShared secret with TURN server(change before production)
TURN_EXTERNAL_HOSTTURN server hostnameturn.atl.network
TURN_PORTTURN/STUN UDP port3478
TURNS_PORTTURN over TLS port5349

Rate limiting variables

VariableDescriptionDefault
PROSODY_C2S_RATEClient connection rate limit10kb/s
PROSODY_C2S_BURSTClient burst allowance25kb
PROSODY_C2S_STANZA_SIZEMax client stanza size (bytes)262144
PROSODY_S2S_RATEServer federation rate limit30kb/s
PROSODY_S2S_BURSTServer burst allowance100kb
PROSODY_S2S_STANZA_SIZEMax server stanza size (bytes)524288
For the complete list of all environment variables, see the Environment Variables reference.

VirtualHost configuration

Prosody uses a single VirtualHost block for the primary domain. The domain is set dynamically from the XMPP_DOMAIN environment variable (mapped to PROSODY_DOMAIN in compose).
-- Domain from environment
local domain = os.getenv("PROSODY_DOMAIN") or "atl.chat"
allow_registration = os.getenv("PROSODY_ALLOW_REGISTRATION") == "true"

VirtualHost(domain)
http_host = __http_host  -- maps HTTP Host header to this VirtualHost

ssl = {
    key = os.getenv("PROSODY_SSL_KEY") or
        ("certs/live/" .. domain .. "/privkey.pem"),
    certificate = os.getenv("PROSODY_SSL_CERT") or
        ("certs/live/" .. domain .. "/fullchain.pem")
}

-- VirtualHost-scoped modules (not loaded globally)
modules_enabled = {
    "http_admin_api",     -- REST API for user account management
    "default_bookmarks",  -- Default bookmarks when user has none
}

-- Default MUC bookmark for new users
default_bookmarks = {
    { jid = "general@muc." .. domain, name = "General", autojoin = true },
}
Key points about the VirtualHost:
  • http_host maps incoming HTTP requests to this VirtualHost, which is important when Prosody sits behind a reverse proxy
  • http_admin_api is loaded only on the VirtualHost (not globally) so the REST API is scoped to this domain
  • default_bookmarks auto-joins new users to the #general MUC room
  • SSL certificates follow the Let’s Encrypt directory layout (certs/live/<domain>/)
  • BOSH/WebSocket URLs are dynamically derived from PROSODY_HTTP_EXTERNAL_URL when set

Component blocks

Components extend Prosody with additional services. Each component gets its own subdomain and SSL certificate.

MUC (multi-user chat)

Component("muc." .. domain) "muc"

modules_enabled = {
    "muc_notifications",      -- Push notifications for MUC events
    "muc_offline_delivery",   -- Offline delivery for MUC events
    "muc_thread_polyfill",    -- Infer thread from XEP-0461 reply
    "pastebin",               -- Intercept large messages, replace with paste URL
    "muc_limits",             -- Rate-limit room events to prevent floods
    "muc_moderation",         -- XEP-0425: Message moderation
    "muc_mam_hints",          -- XEP-0334: Respect store/no-store hints
    "muc_mam_markers",        -- XEP-0333: Archive chat markers in MAM
    "muc_markers",            -- Rewrite message id to stanza-id for reactions
    "muc_defaults",           -- Create default MUCs on startup
    "muc_slow_mode",          -- Per-user rate limit set by room owners
}
The MUC component creates a general room on startup with the admin JID as owner. Room defaults (public, persistent, logged) are controlled via PROSODY_MUC_* environment variables. MUC rate limiting is configured globally:
SettingValueDescription
muc_event_rate0.5Max events/second (one every 2 seconds)
muc_burst_factor6Allow 6× burst for 6 seconds
muc_max_nick_length23Max nickname length
muc_max_char_count5664Max bytes per message
muc_max_line_count23Max lines per message

HTTP file upload

Component("upload." .. domain) "http_file_share"
http_host = __http_host
http_external_url = os.getenv("PROSODY_UPLOAD_EXTERNAL_URL")
    or ("https://upload." .. domain .. "/")
SettingValue
Max file size100 MB
Daily quota per user1 GB
Global quota10 GB
File expiration30 days

SOCKS5 proxy (XEP-0065)

Component("proxy." .. domain) "proxy65"
proxy65_address = os.getenv("PROSODY_PROXY_ADDRESS") or ("proxy." .. domain)
Provides peer-to-peer file transfer proxying. The proxy listens on port 5000 (configurable via PROSODY_PROXY65_PORT).

PubSub with RSS feeds

Component("pubsub." .. domain) "pubsub"
modules_enabled = { "pubsub_feeds" }
feeds = {
    feed = os.getenv("PROSODY_FEED_URL") or "https://allthingslinux.org/feed",
}
The PubSub component pulls RSS/Atom feeds and publishes them as XMPP PubSub nodes. Registered users can create their own PubSub nodes.

Bridge component (XEP-0114)

Component("bridge." .. domain) "component"
component_secret = os.getenv("BRIDGE_XMPP_COMPONENT_SECRET")
    or os.getenv("XMPP_COMPONENT_SECRET")
    or "change_me_xmpp_component_secret"
The bridge service connects as an external XMPP component on port 5347. The component interface listens on all interfaces (component_interfaces = { "*" }) so the bridge container can reach it across the Docker network.
Warning: Change BRIDGE_XMPP_COMPONENT_SECRET from the default before production deployment.

Storage configuration

Prosody uses SQLite by default for development and can be switched to PostgreSQL for production via PROSODY_STORAGE=sql.
default_storage = "sql"

sql = {
    driver = "SQLite3",
    database = "data/prosody.sqlite",
}
Storage is assigned per data type:
Data typeBackendNotes
accounts, roster, vcard, private, blocklistsqlUser data
archive, muc_log, offlinesqlMessage archives
pubsub_nodes, pubsub_data, pepsqlPubSub and PEP
http_file_sharesqlFile upload metadata
caps, carbonsmemoryEphemeral (not persisted)
Data is stored in Docker volumes mapped to data/xmpp/data/ on the host.

TLS and security

Global TLS settings

ssl = {
    protocol = "tlsv1_2+",
    ciphers = "ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS",
    curve = "secp384r1",
    options = { "cipher_server_preference", "single_dh_use", "single_ecdh_use" },
}
This enforces TLS 1.2+ with modern AEAD ciphers and ECDHE key exchange. The certificate directory follows the Let’s Encrypt layout:
/etc/prosody/certs/live/<domain>/
├── fullchain.pem
└── privkey.pem

Encryption enforcement

SettingDefaultRecommendation
c2s_require_encryptiontrueAlways true in production
s2s_require_encryptiontrueAlways true in production
s2s_secure_authtruetrue for certificate-based federation auth
allow_unencrypted_plain_authfalseNever true in production

Authentication

Prosody uses hashed password storage with SCRAM-SHA-256 as the primary SASL mechanism:
authentication = "internal_hashed"
sasl_mechanisms = {
    "SCRAM-SHA-256",
    "SCRAM-SHA-1",
}
SCRAM-SHA-1 is kept for compatibility with older clients. In-band registration is disabled — users are provisioned via the Portal through mod_http_admin_api.

Rate limiting

Three rate limit tiers protect against abuse:
TierRateBurstMax stanza
Client (c2s)10 KB/s25 KB256 KB
Server (s2s)30 KB/s100 KB512 KB
HTTP upload2 MB/s10 MB
Additional protections:
  • max_connections_per_ip: 5 (increase if bridge shares an IP)
  • anti_spam_services: subscribes to xmppbl.org real-time block lists
  • block_registrations_users: blocks common abusive usernames (admin, root, postmaster, etc.)
  • block_registrations_require: enforces ^[a-zA-Z0-9_.-]+$ pattern for usernames

HTTP security headers

Prosody serves HTTP responses with hardened headers:
HeaderValue
Strict-Transport-Securitymax-age=31536000; includeSubDomains; preload
X-Frame-OptionsDENY
X-Content-Type-Optionsnosniff
X-XSS-Protection1; mode=block
Referrer-Policystrict-origin-when-cross-origin
Content-Security-PolicyAllows self and XMPP endpoints (connect-src for BOSH/WebSocket)

Audit findings and recommendations

The following findings are from a configuration audit and remain relevant to the current setup.

Resolved issues

These critical issues from the original audit have been fixed in the current configuration:
  • TLS enforcement defaults: .env.example now ships with PROSODY_C2S_REQUIRE_ENCRYPTION=true, PROSODY_S2S_REQUIRE_ENCRYPTION=true, PROSODY_S2S_SECURE_AUTH=true, and PROSODY_ALLOW_UNENCRYPTED_PLAIN_AUTH=false.
  • Global SSL block: The ssl block is now active (not commented out) with TLS 1.2+ and modern ciphers.
  • Legacy auth removed: legacyauth is no longer in modules_enabled — only SASL (SCRAM-SHA-256/SHA-1) is used.
  • HTTP status endpoint: http_status_allow_cidr is restricted to 172.16.0.0/12 and 127.0.0.0/8 (Docker networks and localhost), not world-open.

Active considerations

  • Archive retention: PROSODY_ARCHIVE_EXPIRES_AFTER defaults to 1y. Monitor SQLite database size over time, especially with many active users.
  • Connections per IP: PROSODY_MAX_CONNECTIONS_PER_IP defaults to 5. In Docker environments where the bridge and other services connect from the same network IP, you may need to increase this value.
  • mod_register for password changes: Registration is disabled (Portal provisions users), but mod_register is also needed for password changes by existing users. Consider enabling it with allow_registration = false if users need self-service password changes.
  • Namespace exclusions: dont_archive_namespaces excludes typing indicators (chatstates) and Jingle call signaling from archives, reducing storage usage.

Positive findings

The configuration exceeds typical Prosody deployments in several areas:
  • Comprehensive module selection covering core protocol, modern messaging (MAM, carbons, smacks), mobile optimization (CSI, cloud_notify), and spam prevention (anti_spam, blocklist, report_forward)
  • Push notification privacy: push_notification_with_body and push_notification_with_sender are both false by default
  • Anti-spam with xmppbl.org RTBL subscription
  • Thorough HTTP security headers including HSTS, CSP, and X-Frame-Options
  • Proper TURN/STUN external configuration for audio/video calls
  • Certificate handling with Let’s Encrypt layout, legacy fallback, and self-signed generation

Customising the configuration

To modify Prosody settings:
  1. Set environment variables in .env (preferred for most settings)
  2. Edit apps/prosody/config/prosody.cfg.lua directly for structural changes
  3. Reload the configuration without restarting:
    just xmpp reload
    
  4. Verify the configuration is valid:
    just xmpp check-config
    
For TLS certificate changes, Prosody needs a reload:
just xmpp reload
To verify certificates are correctly configured:
just xmpp check-certs