Deploy a self-hosted chat platform in one command. Choose between Matrix/Element (federated, E2EE, bridges) or Stoat/Revolt (modern UI, simple setup).
curl -fsSL https://raw.githubusercontent.com/loponai/oneshotmatrix/main/install.sh | sudo bashWe recommend Scala Hosting because their self-managed VPS gives you full root access out of the box — which is required for our Docker, firewall, and SSL configurations. KVM virtualization means Docker runs natively with zero issues.
- Go to Scala Hosting Self-Managed VPS — make sure you're on the Self-Managed (unmanaged) plans, not the Managed ones
- Pick Build #1 (2 cores, 4GB RAM, 50GB NVMe) — $19.95/mo, plenty for a personal/small-community server
- Under OS, select Rocky Linux 10 (the one without SPanel) — this gives you a clean server with nothing to disable later. If you accidentally picked "Rocky Linux 10 with SPanel", see Step 3 to disable it
- Complete checkout and wait for your welcome email with your server IP and root password
You need a domain name that points to your VPS IP address. You can register one through Scala during checkout, or use one you already own.
Important: Do NOT use SPanel or the VPS's built-in nameservers for DNS. In Step 3 we disable SPanel, which would kill the VPS's DNS server and break your domain. Use external DNS instead (Cloudflare is free and works great).
- Find your VPS IP — it's in the welcome email from Scala
- Create a free Cloudflare account if you don't have one
- Add your domain to Cloudflare — it will give you two nameservers (e.g.
ann.ns.cloudflare.com,bob.ns.cloudflare.com) - Update your domain's nameservers — go to my.scalahosting.com > My Domains > click Manage next to your domain > Manage Nameservers > select "Use custom nameservers" and enter the two Cloudflare nameservers
- Add an A record in Cloudflare — go to your domain in the Cloudflare dashboard > DNS > Add Record:
| Type | Name | Content | Proxy status |
|---|---|---|---|
| A | @ (or a subdomain like chat) |
Your VPS IP | DNS only (grey cloud) |
The proxy must be off (grey cloud, "DNS only"). Cloudflare's proxy doesn't support port 8448 (Matrix federation) and blocks SSL certificate generation.
- Wait for nameserver changes to propagate (can take up to 24-48 hours, usually faster)
If you don't want to use Cloudflare, you can create the A record in whatever registrar you bought the domain from (Namecheap, Porkbun, etc.) — just make sure you're not using the VPS as your nameserver.
SSH is how you remotely control your server from a terminal. You type commands on your computer and they run on the VPS.
On Mac/Linux: Open Terminal (it's built in).
On Windows: Open PowerShell (search for it in the Start menu) or install Windows Terminal from the Microsoft Store.
Then connect to your server. Scala uses port 6543 for SSH (not the default 22):
ssh root@YOUR_SERVER_IP -p 6543Replace YOUR_SERVER_IP with the IP from your Scala welcome email (e.g. ssh root@142.248.180.64 -p 6543).
Getting "Connection refused"? If port 6543 doesn't work, try without
-p 6543(some Scala plans use the default port 22). Check your welcome email for the correct SSH port.
- It will ask "Are you sure you want to continue connecting?" — type
yesand press Enter - Enter the root password from your Scala welcome email
- The screen will stay completely blank as you type or paste — no dots, no stars, nothing. This is normal! Just paste your password and press Enter. It's there, you just can't see it.
Once you're in, you'll see a command prompt on your server.
If you picked "Rocky Linux 10" (without SPanel) in Step 1, you're good — skip straight to Step 4.
If you picked "Rocky Linux 10 with SPanel", disable SPanel's web server so our installer can use ports 80/443:
systemctl mask --now httpd
systemctl mask --now nginxThis disables SPanel's web admin UI, but you won't need it — everything is managed via SSH after the installer runs.
curl -fsSL https://raw.githubusercontent.com/loponai/oneshotmatrix/main/install.sh | sudo bashYou'll be asked for:
- Platform — Matrix/Element or Stoat (Revolt)
- Domain — the domain you set up in Step 2
- Email — for SSL certificates
- Admin password — (Matrix only) for the
@adminaccount - Bridges — (Matrix only) optional Discord and Telegram bridges
The installer handles Docker, firewall, SSL, and all configuration automatically.
Open your domain in a browser — or install it as a desktop app (see Desktop & Mobile Apps below).
- Element Web — Modern chat UI at your domain
- Synapse — Matrix homeserver with federation
- PostgreSQL — Production database
- Coturn — TURN/STUN for voice and video calls
- Nginx — Reverse proxy with auto HTTPS
- Discord Bridge (optional) — Access Discord from Element
- Telegram Bridge (optional) — Access Telegram from Element
- Stoat Web Client — Discord-like chat UI
- Stoat API — Rust backend with MongoDB
- Caddy — Reverse proxy with auto HTTPS
- File uploads, push notifications, URL previews built in
Why does the app say "Revolt"? Stoat was previously called Revolt before a rebrand in late 2025. The web client hasn't been updated with the new branding yet. Same software, same team, just a new name.
"API error" on first load? This is normal — the services take 30-60 seconds to fully start after the installer finishes. Wait a moment and refresh the page.
| Feature | Why it matters |
|---|---|
| Full root access | Required for Docker, firewall, and SSL configuration |
| KVM virtualization | Docker runs natively — no issues like OpenVZ |
| Unmetered bandwidth | No surprise bills from federation traffic |
| NVMe storage | Fast database reads/writes for Synapse + PostgreSQL |
| Free snapshots | Backup before changes, roll back if needed |
| Scalable | Add RAM ($3/GB) or CPU ($10/core) anytime |
Recommended plan: Self-Managed Build #1 — 2 cores, 4GB RAM, 50GB NVMe at $19.95/mo. Step up to Build #2 (4 cores, 8GB) for heavier usage.
Discord — Open Element, DM @discordbot:yourdomain.com:
!discord login
Follow the prompts. Your Discord servers will appear as Matrix rooms.
Telegram — DM @telegrambot:yourdomain.com:
!tg login
Enter your phone number and verification code. Telegram chats sync to Matrix.
Matrix — Public registration is off by default. Create accounts with:
cd /opt/matrix-discord-killer
docker compose exec synapse register_new_matrix_user -c /data/homeserver.yamlStoat — Open the web client and register. First account becomes server owner.
You don't have to use the browser — Element has apps for every platform, and they all support custom homeservers out of the box.
Desktop (Windows, macOS, Linux):
- Download Element Desktop for your platform
- On the login screen, click "Edit" next to the homeserver URL
- Change it to
https://yourdomain.com - Log in with your account
Mobile:
- Android — Google Play or F-Droid
- iOS — App Store
- On the login screen, tap "Edit" next to the homeserver and enter
https://yourdomain.com
Install as a PWA (alternative):
If you prefer not to install an app, you can turn the web client into a standalone desktop window:
- Chrome/Edge: Visit your domain → click the install icon in the address bar (or menu → "Install app")
cat /opt/matrix-discord-killer/credentials.txtAfter installation, open your domain in a browser and log in with the admin account you created during setup.
- Click the + next to any section in the left sidebar
- Choose New room
- Give it a name and optionally set it to private (encrypted by default)
- Your room appears in the sidebar, ready for messages
Spaces are groups of rooms — think of them as folders or communities.
- Click the + in the left sidebar → Create a space
- Name it and choose public or private
- Add existing rooms or create new ones inside the space
You need to create accounts for them first (public registration is off by default):
cd /opt/matrix-discord-killer
docker compose exec synapse register_new_matrix_user -c /data/homeserver.yamlThen share your server address — they log in at https://yourdomain.com or using any Matrix app pointed at your server.
Private rooms are encrypted by default. Element will prompt you to set up a Security Key (cross-signing) on first login — do this, it protects your message history if you log in from a new device.
Click the phone or camera icon in any room. Calls use your Coturn server (set up by the installer) for NAT traversal. Group calls work in rooms with video room features enabled.
cd /opt/matrix-discord-killer
docker compose ps # See what's running
docker compose logs synapse # Check Matrix server logs
docker compose logs nginx # Check reverse proxy logs
docker compose logs coturn # Check voice/video relay logs
docker compose restart # Restart everything
docker compose down # Stop everything
docker compose up -d # Start everythingcd /opt/matrix-discord-killer
docker compose pull
docker compose up -dPublic registration is off by default (recommended). Create accounts from the command line:
cd /opt/matrix-discord-killer
docker compose exec synapse register_new_matrix_user -c /data/homeserver.yamlIt will ask for a username, password, and whether to make them an admin.
If you want anyone to be able to sign up without you creating accounts:
- Edit the config:
nano /opt/matrix-discord-killer/data/synapse/homeserver.yaml
- Change this line:
enable_registration: true
- Restart Synapse:
cd /opt/matrix-discord-killer && docker compose restart synapse
Warning: Open registration means anyone can create accounts on your server. Consider adding a CAPTCHA or rate limiting if you enable this.
Federation lets your users communicate with people on other Matrix servers (like matrix.org).
# Check if federation is working
curl -sf https://yourdomain.com/.well-known/matrix/server
# Should return: {"m.server": "yourdomain.com:443"}
# Or use the online tester
# https://federationtester.matrix.org/#yourdomain.comAll persistent data lives in /opt/matrix-discord-killer/data/. To backup:
cd /opt/matrix-discord-killer
docker compose down
tar -czf ~/matrix-backup-$(date +%Y%m%d).tar.gz data/ .env docker-compose.yml
docker compose up -dCertificates auto-renew via a cron job. To check or manually renew:
# Check when the cert expires
certbot certificates
# Manual renewal
certbot renew --webroot -w /opt/matrix-discord-killer/data/certbot/www
cd /opt/matrix-discord-killer && docker compose restart nginx coturn| File | What it does |
|---|---|
data/synapse/homeserver.yaml |
Main Synapse config — registration, federation, database |
.env |
Domain, secrets, bridge toggles |
credentials.txt |
Your saved admin login and secrets |
docker-compose.yml |
Container definitions |
data/synapse/media_store/ |
Uploaded files, avatars, media |
data/postgres/ |
PostgreSQL database (messages, accounts) |
data/coturn/turnserver.conf |
Voice/video relay configuration |
data/nginx/matrix.conf |
Nginx reverse proxy rules |
After installation, open your domain in a browser and register your account. The first account becomes the server owner.
- Click the + button in the left sidebar
- Choose Create a server
- Give it a name — this is your community space
- Your server appears in the sidebar with a default General channel
- Click the + next to "Text Channels" (or "Voice Channels") in your server
- Name the channel and click Create
- Quick invite: Right-click any channel name → Create Invite — copy and share the link
- Manage invites: Go to Server Settings → Invites to view, copy, or revoke existing invite links
If you enabled
invite_only = true(see Admin Guide below), people will need an invite link to register. If registration is open, they can just visit your domain and sign up.
- Server icon: Server Settings → Overview → click the server icon to upload
- Roles & permissions: Server Settings → Roles → create roles and assign permissions
- Categories: Right-click in the channel list → Create Category to organize channels into groups
cd /opt/matrix-discord-killer
docker compose ps # See what's running
docker compose logs api # Check API logs
docker compose logs caddy # Check reverse proxy logs
docker compose restart # Restart everything
docker compose down # Stop everything
docker compose up -d # Start everythingcd /opt/matrix-discord-killer
docker compose pull
docker compose up -dBy default anyone who visits your domain can register. To lock it down:
- Edit the config:
nano /opt/matrix-discord-killer/Revolt.toml
- Add this line (or change it if it already exists):
invite_only = true
- Restart the API:
cd /opt/matrix-discord-killer && docker compose restart api
By default, Stoat cannot send emails — there is no built-in mail server. This means "Forgot password" won't work until you configure SMTP. You need credentials from an email provider:
| Provider | Free tier | Sign up |
|---|---|---|
| Brevo (Sendinblue) | 300 emails/day | brevo.com |
| Resend | 100 emails/day | resend.com |
| Mailgun | 100 emails/day | mailgun.com |
| Gmail | Use with app password | myaccount.google.com/apppasswords |
Once you have SMTP credentials, add this to your Revolt.toml:
- Edit the config:
nano /opt/matrix-discord-killer/Revolt.toml
- Add the
[api.smtp]section (replace the values with your provider's details):[api.smtp] host = "smtp.brevo.com" port = 587 username = "your-smtp-username" password = "your-smtp-password" from_address = "noreply@yourdomain.com" use_tls = true
- Restart the API:
cd /opt/matrix-discord-killer && docker compose restart api
Gmail example: Use
host = "smtp.gmail.com",port = 587, your Gmail address asusername, and an app password (not your regular Gmail password) aspassword.
After this, password reset emails, email verification, and other notifications will work.
All persistent data lives in /opt/matrix-discord-killer/data/. To backup:
cd /opt/matrix-discord-killer
docker compose down
tar -czf ~/stoat-backup-$(date +%Y%m%d).tar.gz data/ .env Revolt.toml
docker compose up -d| File | What it does |
|---|---|
Revolt.toml |
Main config — features, limits, invite-only mode |
.env |
Domain, secrets, encryption keys |
credentials.txt |
Your saved setup info |
docker-compose.stoat.yml |
Container definitions |
data/db/ |
MongoDB database (messages, accounts) |
data/minio/ |
Uploaded files and media |
The web client works great in a browser, but you can also get a desktop-app experience:
Install as a desktop app (recommended):
This turns your self-hosted web client into a standalone window — no browser tabs, no URL bar, just your chat.
- Chrome/Edge: Visit your domain → click the install icon in the address bar (or ⋮ menu → "Install app" / "Create shortcut" → check "Open as window")
- Firefox: Not supported natively — use Chrome or Edge for this
The installed app launches from your Start menu / Applications folder like any other program, and it's already connected to your server.
Official Stoat desktop app:
Stoat also has a standalone desktop app for Windows, macOS, and Linux. This app connects to the official stoat.chat servers by default. To point it at your self-hosted server, launch it with:
# Windows (from the install directory)
stoat-desktop.exe --force-server https://yourdomain.com
# macOS
/Applications/Stoat.app/Contents/MacOS/Stoat --force-server https://yourdomain.com
# Linux
./stoat-desktop --force-server https://yourdomain.comThe
--force-serverflag is a developer option and may not work reliably in all versions. The PWA install method above is simpler and always works.
Mobile:
- Android (beta) — Google Play
- iOS (beta) — TestFlight
- Mobile apps may not support custom server URLs yet. As an alternative, use your phone's browser and "Add to Home Screen" for an app-like experience.
cd /opt/matrix-discord-killer
docker compose ps # Service status
docker compose logs # All logs
docker compose logs synapse # Single serviceFederation not working? Check DNS (dig A yourdomain.com), test at https://federationtester.matrix.org, verify port 8448 is open (ufw status on Ubuntu/Debian, firewall-cmd --list-ports on Rocky Linux).
Voice/video failing? Check docker compose logs coturn, verify TURN ports open (ufw status or firewall-cmd --list-ports), test in Element under Settings > Voice & Video.
SSL issues?
certbot renew --webroot -w /opt/matrix-discord-killer/data/certbot/www
cd /opt/matrix-discord-killer && docker compose restart nginx coturnStoat not loading? Check docker compose logs caddy and docker compose logs api.
Stoat uploads failing? Check docker compose logs minio and docker compose logs autumn.
sudo /opt/matrix-discord-killer/uninstall.shPermanently destroys all data including messages, accounts, and media.
- Ubuntu 22.04+, Debian 12+, or Rocky Linux 8+ on an unmanaged VPS with full root access (4GB RAM recommended)
- A domain name with DNS pointed to your server
- Ports 80/443 free (if SPanel is installed, disable it first — see Step 3)
Matrix/Element:
Internet → Nginx (80/443/8448)
├→ Element Web (/)
├→ Synapse (/_matrix/)
│ └→ PostgreSQL
├→ Coturn (voice/video)
├→ mautrix-discord (optional)
└→ mautrix-telegram (optional)
Stoat (Revolt):
Internet → Caddy (80/443)
├→ Web client, API, WebSocket
├→ File server, URL proxy
└→ MongoDB, Redis, RabbitMQ, MinIO
| Port | Purpose |
|---|---|
| 80 | HTTP → HTTPS redirect + ACME |
| 443 | Element/Synapse or Stoat client |
| 8448 | Matrix federation (Matrix only) |
| 3478 | TURN TCP/UDP (Matrix only) |
| 5349 | TURNS TCP/UDP (Matrix only) |
| 49152-49200 | TURN relay media UDP (Matrix only) |
Stoat only needs ports 80 and 443.
/opt/matrix-discord-killer/
├── docker-compose.yml / docker-compose.stoat.yml
├── .env # Generated secrets
├── credentials.txt # Login details
├── setup.sh / uninstall.sh
├── templates/ # Config templates
└── data/ # All persistent data