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:
| Service | Image | Purpose |
|---|---|---|
| API | ghcr.io/stoatchat/api:v0.11.5 | REST API backend |
| Events | ghcr.io/stoatchat/events:v0.11.5 | WebSocket server (real-time messaging) |
| File Server | ghcr.io/stoatchat/file-server:v0.11.5 | File upload/download (Autumn) |
| Proxy | ghcr.io/stoatchat/proxy:v0.11.5 | Link previews and image proxy (January) |
| GIF Proxy | ghcr.io/stoatchat/gifbox:v0.11.5 | Tenor GIF search proxy |
| Cron Daemon | ghcr.io/stoatchat/crond:v0.11.5 | Scheduled cleanup tasks |
| Push Daemon | ghcr.io/stoatchat/pushd:v0.11.5 | Web push notifications |
| Voice Ingress | ghcr.io/stoatchat/voice-ingress:v0.11.5 | Voice channel routing |
| LiveKit | ghcr.io/stoatchat/livekit-server:v1.9.6 | Voice/video infrastructure |
| Web Frontend | ghcr.io/stoatchat/for-web:v0.11.5 | Solid.js web client |
| MongoDB | mongo:7 | Primary database |
| Redis | redis:7-alpine | Cache and pub/sub |
| RabbitMQ | rabbitmq:3-management-alpine | Internal message broker |
| MinIO | minio/minio | S3-compatible file storage |
| Caddy | caddy:2-alpine | Reverse 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 variableslivekit.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
- Open
https://chat.yourdomain.comin your browser - Create the first account — this becomes the instance owner
- Create a server (equivalent to a Discord server)
- Create channels within the server
- 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:
| Path | Backend |
|---|---|
/api/ | api:14009 |
/ws | events: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:
| What | Path | Why |
|---|---|---|
| MongoDB data | ./data/db/ | All messages, users, servers, channels |
| MinIO data | ./data/minio/ | All uploaded files and attachments |
| Configuration | Revolt.toml, secrets.env, .env.web | Instance 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
| Metric | Value |
|---|---|
| Minimum RAM | 2 GB |
| Recommended RAM | 4 GB (with voice) |
| CPU | 2+ cores |
| Idle disk usage | ~2 GB (images + databases) |
| Containers | 15 |
| Network ports | 80, 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.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments