How to Self-Host Stoat with Docker Compose

What Is Stoat?

Stoat started life as Revolt, a Discord alternative built from scratch in Rust. After receiving a cease-and-desist in early 2026, the project renamed to Stoat and moved to github.com/stoatchat. The core product hasn’t changed — it’s a real-time chat platform with text channels, voice calls (via LiveKit), file sharing, bots, custom emoji, and a web/desktop/mobile client. The architecture is microservices-based: separate Rust binaries handle the API, WebSocket events, file storage, push notifications, and media proxying. AGPL-3.0 licensed.

Unlike Matrix or XMPP, Stoat doesn’t federate. It’s a single-server platform designed to feel like Discord — server invites, role permissions, channel categories, and a familiar UI. If your goal is to replace a Discord server for your team or community without federation requirements, Stoat is the closest self-hosted match.

Quick Verdict

Stoat is the most Discord-like self-hosted chat platform available. Voice channels, file sharing, GIF search, bots, and a polished web UI all work out of the box. The trade-off is complexity — 14 containers is a lot to manage, and the project is young (v0.11.x). For teams that need a private Discord replacement and don’t need federation, it’s the best option. For simpler needs, consider Mattermost or Rocket.Chat.

Architecture Overview

Stoat runs as a microservices stack. Each Rust binary handles one concern:

ServiceImagePurpose
APIghcr.io/stoatchat/api:v0.11.5REST API backend
Eventsghcr.io/stoatchat/events:v0.11.5WebSocket server (real-time messaging)
File Serverghcr.io/stoatchat/file-server:v0.11.5File upload/download (Autumn)
Proxyghcr.io/stoatchat/proxy:v0.11.5Link previews and image proxy (January)
GIF Proxyghcr.io/stoatchat/gifbox:v0.11.5Tenor GIF search proxy
Cron Daemonghcr.io/stoatchat/crond:v0.11.5Scheduled cleanup tasks
Push Daemonghcr.io/stoatchat/pushd:v0.11.5Web push notifications
Voice Ingressghcr.io/stoatchat/voice-ingress:v0.11.5Voice channel routing
LiveKitghcr.io/stoatchat/livekit-server:v1.9.6Voice/video infrastructure
Web Frontendghcr.io/stoatchat/for-web:v0.11.5Solid.js web client
MongoDBmongo:7Primary database
Redisredis:7-alpineCache and pub/sub
RabbitMQrabbitmq:3-management-alpineInternal message broker
MinIOminio/minioS3-compatible file storage
Caddycaddy:2-alpineReverse proxy and HTTPS

Prerequisites

  • A Linux server (Ubuntu 22.04+ recommended)
  • Docker and Docker Compose installed (guide)
  • 2 GB RAM minimum (4 GB recommended with voice)
  • 10 GB free disk space (grows with file uploads)
  • A domain name with DNS configured
  • Ports 80, 443, 7881/TCP, and 50000-50100/UDP open

Docker Compose Configuration

Stoat provides an automated setup script. Clone the repository and run the config generator:

git clone https://github.com/stoatchat/self-hosted stoat
cd stoat
chmod +x ./generate_config.sh
./generate_config.sh chat.yourdomain.com

This generates four files:

  • Revolt.toml — Main configuration (shared by all services)
  • secrets.env — VAPID keys, encryption keys, LiveKit credentials
  • .env.web — Frontend environment variables
  • livekit.yml — LiveKit server configuration

Review Revolt.toml before starting. Key settings to verify:

# Domain must match your DNS
[api]
hostname = "chat.yourdomain.com"

# Email verification (disabled by default for self-hosted)
[api.registration]
email_verification = false
invite_only = false

Then start the stack:

docker compose up -d

First startup pulls ~2 GB of images and initializes MongoDB. Wait 1-2 minutes before accessing the web UI.

Manual Configuration (if not using generate_config.sh)

If you need to configure manually, create secrets.env:

# Generate VAPID keys for push notifications
npx web-push generate-vapid-keys

# Generate file encryption key
openssl rand -hex 32

# Generate LiveKit credentials
openssl rand -hex 16  # key
openssl rand -hex 32  # secret

Populate secrets.env:

# Push notification VAPID keys (from web-push output)
PUSHD_VAPID_PRIVATEKEY=your_private_key_here
PUSHD_VAPID_PUBLICKEY=your_public_key_here

# File encryption
FILES_ENCRYPTION_KEY=your_hex_key_here

# LiveKit voice/video
LIVEKIT_WORLDWIDE_KEY=your_livekit_key
LIVEKIT_WORLDWIDE_SECRET=your_livekit_secret

Initial Setup

  1. Open https://chat.yourdomain.com in your browser
  2. Create the first account — this becomes the instance owner
  3. Create a server (equivalent to a Discord server)
  4. Create channels within the server
  5. Generate invite links to share with your team

There is no separate admin panel. Instance administration happens through the API and server settings in the web UI.

Configuration

Invite-Only Mode

To restrict registration to invited users only, edit Revolt.toml:

[api.registration]
invite_only = true

Restart the API service: docker compose restart api

File Upload Limits

MinIO handles file storage. Default upload limits are configured in Revolt.toml. Adjust storage volume size based on your expected file upload volume.

Custom Domain

The Caddyfile in the repository handles HTTPS termination. Caddy automatically provisions Let’s Encrypt certificates. If you use a different reverse proxy, configure it to proxy:

PathBackend
/api/api:14009
/wsevents:9000
/autumn/file-server:3000
/january/proxy:7000
/gifbox/gifbox:14008
/web:5000

Firewall Rules

Voice channels require additional ports:

# HTTPS and HTTP
ufw allow 80/tcp
ufw allow 443/tcp

# LiveKit signaling
ufw allow 7881/tcp

# LiveKit media (voice/video RTP)
ufw allow 50000:50100/udp

Backup

Back up these directories and files:

WhatPathWhy
MongoDB data./data/db/All messages, users, servers, channels
MinIO data./data/minio/All uploaded files and attachments
ConfigurationRevolt.toml, secrets.env, .env.webInstance settings and secrets

MongoDB backup:

docker compose exec mongodb mongodump --out /data/backup/$(date +%Y%m%d)
docker cp stoat-mongodb-1:/data/backup/ ./backups/

See Backup Strategy for a complete 3-2-1 backup approach.

Troubleshooting

Web UI Shows Blank Page

Symptom: Browser loads but shows nothing after docker compose up. Fix: Wait 1-2 minutes for all services to initialize. Check docker compose logs web for errors. Verify .env.web has correct VITE_API_URL matching your domain.

Voice Channels Don’t Connect

Symptom: Clicking a voice channel shows “connecting” indefinitely. Fix: Ensure ports 7881/TCP and 50000-50100/UDP are open in your firewall and forwarded if behind NAT. Check docker compose logs livekit-server for errors.

File Uploads Fail

Symptom: Uploading images or files returns an error. Fix: Check MinIO is running: docker compose logs minio. Verify the FILES_ENCRYPTION_KEY in secrets.env matches what’s in Revolt.toml. If you re-ran generate_config.sh, existing secrets may have been overwritten — restore from backup.

Messages Not Delivering in Real-Time

Symptom: Messages appear only after page refresh. Fix: The Events WebSocket service may be down. Check docker compose logs events. Also verify RabbitMQ is healthy: docker compose logs rabbitmq.

Registration Not Working

Symptom: Signup form returns an error. Fix: Check if invite_only = true in Revolt.toml. If email verification is enabled, ensure SMTP settings are configured. For self-hosted instances, disabling email verification (email_verification = false) is common.

Resource Requirements

MetricValue
Minimum RAM2 GB
Recommended RAM4 GB (with voice)
CPU2+ cores
Idle disk usage~2 GB (images + databases)
Containers15
Network ports80, 443, 7881/TCP, 50000-50100/UDP

Verdict

Stoat is the closest you’ll get to self-hosting Discord. The UI is polished, voice channels work via LiveKit, and the Rust backend is performant. The downsides are real: 15 containers is a maintenance burden, the project is pre-1.0 (v0.11.x), documentation is thin, and there’s no federation. If your community needs voice channels and a Discord-like UX, Stoat is the right tool. If text chat is enough, Mattermost or Matrix with Element are simpler to run.

Frequently Asked Questions

Is Stoat the same as Revolt?

Yes. Stoat was originally called Revolt. The project renamed to Stoat in early 2026 after receiving a cease-and-desist. The codebase, features, and development team are the same — only the name and GitHub organization changed (now github.com/stoatchat).

Does Stoat support federation?

No. Unlike Matrix or XMPP, Stoat is a single-server platform. All users, channels, and data live on one instance. If federation is a requirement, use Matrix with Element instead.

Can I migrate from Discord to Stoat?

There’s no automated migration tool. You’ll need to manually recreate servers, channels, and roles in Stoat. Message history from Discord cannot be imported. Stoat’s UI and concepts (servers, channels, roles, invites) closely mirror Discord, so the transition for users is intuitive.

How much RAM does Stoat need?

Minimum 2 GB, but 4 GB is recommended if you plan to use voice channels. The 15-container architecture (MongoDB, Redis, RabbitMQ, MinIO, LiveKit, plus Rust services) adds up. Without voice, 2 GB works for small communities.

Does Stoat have mobile apps?

Yes. Stoat has a mobile web app and native apps for Android and iOS, though the mobile experience is less polished than the web client. The web UI works well on mobile browsers as a fallback.

How does Stoat compare to Mattermost?

Mattermost is simpler to deploy (2-3 containers vs 15), more mature, and better for team communication. Stoat is more Discord-like with voice channels, server/channel hierarchy, and a consumer-oriented UI. Choose Mattermost for workplace chat; choose Stoat for community servers that need a Discord-like experience.

Comments