How to Deploy Rocket.Chat on a VPS
Updated Sep 2026
verified on Ubuntu 26.04 · Sep 2026Self-host Rocket.Chat on your own VPS with the official rocketchat-compose repo — MongoDB replica set, NATS, bundled Traefik with Let's Encrypt, a pinned release, and the setup wizard that creates your admin.
- A VPS with 4 vCPU / 8 GB RAM for a production team (upstream sizes the app and MongoDB at 4 GB each)
- A fresh Ubuntu 24.04 server with root/sudo SSH access (see the MongoDB note on 26.04 below)
- A domain you can point at the server, e.g. chat.example.com
- Docker Engine + Compose installed (see the base guide below)
- git on the server — the official deployment is a repository you clone
What Rocket.Chat is
Rocket.Chat is an MIT-licensed team chat platform: channels, direct messages, threads, and an omnichannel inbox for customer conversations, with voice and video added through marketplace apps. It is a Node.js application backed by MongoDB, and it is the most extensible of the self-hosted Slack alternatives — which is also why its official deployment is bigger than most chat apps.
Upstream's supported Docker path is the rocketchat-compose repository. It is not one compose file but several you stack together:
compose.yml— Rocket.Chat itselfcompose.database.yml— MongoDB (officialmongodb-community-serverimage) as a single-node replica set, plus an exportercompose.nats.yml— NATS, the message bus Rocket.Chat uses between instancescompose.traefik.yml— Traefik as the reverse proxy, with Let's Encryptcompose.monitoring.yml— Prometheus, Grafana, Loki and a log collectordocker.yml— Docker-specific overrides (health check, log collection)
This guide follows that path as written, because it's what upstream tests and what their update notes assume. If you're choosing between chat servers first, Mattermost vs Rocket.Chat covers the trade-offs, and Mattermost and Zulip have their own guides.
Server sizing
Upstream's system requirements for the smallest tier (up to 500 concurrent users) list 2 vCPU / 4 GiB RAM / 20 GiB disk for Rocket.Chat and 2 vCPU / 4 GiB RAM / 10 GiB disk for MongoDB. Put both on one box and that is an 8 GB RAM server — which is why we list 8 GB as the minimum.
For reference, the whole stack (app, MongoDB, NATS, Traefik and the monitoring containers) measured ~1,290 MB of RAM at idle on our test box, with ~5.7 GB of disk used after install. Idle is not load: MongoDB grows its cache as the message history grows, and file uploads land in the database by default (GridFS). Give it the headroom.
A note on Ubuntu 26.04: Rocket.Chat's docs warn that MongoDB 8.x may fail to
start on Ubuntu 26.04 because MongoDB 8.0+ is incompatible with Linux kernel
6.19. Rocket.Chat 8.x requires MongoDB 8.0 or later, so you cannot sidestep it
with an older MongoDB. Upstream's advice is Ubuntu 24.04 LTS for now; if you are
on 26.04, check the kernel (uname -r) before you start.
Prepare the server
This guide assumes Docker Engine and the Compose plugin are installed, along
with a non-root user and a ufw firewall. If not, work through
Docker & Compose on Ubuntu first.
Open SSH and the web ports only:
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw --force enable
sudo ufw status verbose
One caveat worth knowing before the install: ports that Docker publishes
bypass ufw. Upstream's compose.yml publishes Rocket.Chat's port 3000 (and
its metrics port 9458) on 0.0.0.0 by default. The config below binds them to
loopback instead, so Traefik is the only public way in.
Paid link — we earn a commission if you shop through it.
Install Rocket.Chat
Clone the official repository and create your .env from the example:
sudo apt-get install -y git
cd ~
[ -d rocketchat-compose ] || git clone --depth 1 https://github.com/RocketChat/rocketchat-compose.git
cd ~/rocketchat-compose
[ -f .env ] || cp .env.example .env
Now edit .env. Four changes before the first start:
RELEASE— pin an exact version. The example ships an older default (8.0.1); upstream is explicit that production should use fixed versions, neverlatest. At the time of writing the current release is 8.8.1.BIND_IP=127.0.0.1— keeps ports 3000 and 9458 off the public interface.GRAFANA_ADMIN_PASSWORD— the example shipsrc-admin. Replace it with a generated one. Grafana only reads it on first start, so do it now.COMPOSE_FILE— a standard Docker Compose variable that lists the files to stack. Setting it means later commands (docker compose ps,pull,logs) see the whole stack without the six-fflags.
cd ~/rocketchat-compose
setenv() { if grep -q "^$1=" .env; then sed -i "s|^$1=.*|$1=$2|" .env; else echo "$1=$2" >> .env; fi; }
setenv RELEASE 8.8.1
setenv BIND_IP 127.0.0.1
grep -q '^GRAFANA_ADMIN_PASSWORD=rc-admin$' .env && setenv GRAFANA_ADMIN_PASSWORD "$(openssl rand -hex 24)"
setenv COMPOSE_FILE compose.monitoring.yml:compose.traefik.yml:compose.database.yml:compose.yml:compose.nats.yml:docker.yml
chmod 600 .env
grep -E '^(RELEASE|BIND_IP|DOMAIN|ROOT_URL|LETSENCRYPT_ENABLED|TRAEFIK_PROTOCOL)=' .env
Leave DOMAIN=localhost, TRAEFIK_PROTOCOL=http and LETSENCRYPT_ENABLED=false
for this first start. Leave LETSENCRYPT_EMAIL filled in, too: Traefik refuses
to start with it empty, even when Let's Encrypt is off.
Start the stack with upstream's command:
cd ~/rocketchat-compose
docker compose -f compose.monitoring.yml -f compose.traefik.yml -f compose.database.yml -f compose.yml -f compose.nats.yml -f docker.yml up -d
The first start pulls a dozen images, starts MongoDB, and an init container runs
rs.initiate to create the replica set Rocket.Chat needs. Rocket.Chat then
creates its database on first connect. Expect a few minutes before it answers.
Poll until it does:
cd ~/rocketchat-compose
for i in $(seq 1 60); do curl -fsS http://127.0.0.1:3000/api/info && break; sleep 10; done
echo
docker compose ps
A JSON response from /api/info means the server is up. It is only
reachable from the box itself: port 3000 is on loopback, and Traefik only routes
requests whose Host matches DOMAIN, which is still localhost. That's
deliberate. The first person to finish the setup wizard becomes the
administrator, so you don't want the wizard on a public IP before you get to it.
HTTPS + domain with the bundled Traefik
Point an A record for chat.example.com at the server's public IP and wait
for it to resolve. Then switch .env to your domain and turn on Let's Encrypt.
Traefik uses the TLS challenge on port 443, so that port must be reachable:
cd ~/rocketchat-compose
sed -i \
-e 's|^DOMAIN=.*|DOMAIN=chat.example.com|' \
-e 's|^ROOT_URL=.*|ROOT_URL=https://chat.example.com|' \
-e 's|^LETSENCRYPT_ENABLED=.*|LETSENCRYPT_ENABLED=true|' \
-e 's|^LETSENCRYPT_EMAIL=.*|LETSENCRYPT_EMAIL=you@example.com|' \
-e 's|^TRAEFIK_PROTOCOL=.*|TRAEFIK_PROTOCOL=https|' \
.env
docker compose up -d
ROOT_URL must match the address people actually use, scheme included.
Rocket.Chat builds links, OAuth callbacks and email URLs from it, and a
mismatch produces a warning banner and broken links. Grafana is served on the same
domain under /grafana (the GRAFANA_PATH default). Log in as admin with the
password you generated: grep GRAFANA_ADMIN_PASSWORD ~/rocketchat-compose/.env.
Alternative: Caddy instead of Traefik
If you already run Caddy on the box, the
repo supports dropping the proxy. Upstream's README says to leave a component's
compose file out of the command to exclude it, and the docs show the stack
started without compose.traefik.yml for an Nginx setup. With BIND_IP already
on loopback, that's a two-line change:
cd ~/rocketchat-compose
sed -i \
-e 's|^COMPOSE_FILE=.*|COMPOSE_FILE=compose.monitoring.yml:compose.database.yml:compose.yml:compose.nats.yml:docker.yml|' \
-e 's|^ROOT_URL=.*|ROOT_URL=https://chat.example.com|' \
.env
docker compose up -d --remove-orphans
Then the whole Caddyfile entry is:
chat.example.com {
reverse_proxy 127.0.0.1:3000
}
Caddy's reverse_proxy passes WebSocket upgrades through without extra
config. In this setup Grafana is not proxied. It stays
on 127.0.0.1:5050 (GRAFANA_BIND_IP/GRAFANA_HOST_PORT), so reach it over an
SSH tunnel.
The setup wizard
Open https://chat.example.com. The first load runs a one-time wizard:
- Admin information: full name, username, email and password. This account is the workspace administrator. Use a real mailbox and a long, generated password.
- Organization information: organization name, industry, size and country.
- Register your workspace: Rocket.Chat asks for an email, acceptance of its terms and privacy policy, and a click on Register workspace. A confirmation link is sent to that address. This connects the workspace to Rocket.Chat Cloud.
After you confirm, you land in the workspace as the admin. The wizard appears only once, so finish it in one sitting.
Securing it
- Keep ports 3000 and 9458 on loopback. Check with
sudo ss -tlnp | grep -E ':(3000|9458)'— both should show127.0.0.1. - MongoDB has no password (
ALLOW_EMPTY_PASSWORD=yesin upstream's compose). It is safe only because the port is bound to127.0.0.1(MONGODB_BIND_IP). Never change that bind to0.0.0.0. - Leave the Traefik dashboard off. Upstream publishes port 8080 for it, but
the API stays disabled unless you set
TRAEFIK_API_INSECURE=true. Don't. - Review account registration in the admin settings before you share the URL, and decide whether people can sign up themselves or only by invite.
Backups
Messages, settings, users and (by default) uploaded files all live in MongoDB,
so a MongoDB dump is the backup. This is upstream's mongodump --archive,
compressed and dated, plus a copy of .env:
cd ~/rocketchat-compose
mkdir -p ~/backups
docker compose exec -T mongodb sh -c 'mongodump --archive' | gzip > ~/backups/rocketchat-$(date +%F).archive.gz
cp .env ~/backups/rocketchat-env-$(date +%F)
chmod 600 ~/backups/rocketchat-env-*
ls -lh ~/backups
Copy both off the box. A backup on the same disk as the database protects you from mistakes, not from losing the server. If you moved file uploads to S3 or MinIO, back up that bucket separately.
To restore, stream the archive back in with mongorestore:
cd ~/rocketchat-compose
gunzip -c ~/backups/rocketchat-YYYY-MM-DD.archive.gz | docker compose exec -T mongodb sh -c 'mongorestore --archive'
docker compose restart rocketchat
Upgrades
Each Rocket.Chat version is supported for six months after release, so plan to
upgrade regularly. Read the release notes and upstream's update guidelines
before a version jump, and take a backup first. The routine is: update the
compose repo, change RELEASE in .env, pull, and recreate:
cd ~/rocketchat-compose
git pull --ff-only
docker compose pull
docker compose up -d
for i in $(seq 1 60); do curl -fsS http://127.0.0.1:3000/api/info && break; sleep 10; done
As written, that re-pulls your pinned version. To move to a new release, first
set it, e.g. sed -i 's|^RELEASE=.*|RELEASE=8.9.0|' .env (use the real version
number from the releases page). MongoDB is upgraded the same way through
MONGODB_VERSION (then docker compose up -d). Upstream documents it as a
separate step from the app upgrade.
Troubleshooting
curl on port 3000 never answers. Run docker compose ps and read the
state of mongodb and mongodb-init-container. If MongoDB is restarting,
read docker compose logs --tail 100 mongodb. On Ubuntu 26.04 this is
usually the kernel 6.19 incompatibility described above. If MongoDB is healthy
but Rocket.Chat keeps restarting, read docker compose logs --tail 100 rocketchat.
The replica set must be initiated before Rocket.Chat can connect.
Compose errors with need email for cert expiry notifications. Traefik's
compose file refuses to start without LETSENCRYPT_EMAIL, even with Let's Encrypt disabled. Put any address
back in .env.
The site loads over the IP but not the domain (404), or vice versa. Traefik
routes only on Host(DOMAIN). Check that DOMAIN in .env is the bare
hostname (no https://) and that you ran docker compose up -d after changing
it.
No certificate / browser TLS error. LETSENCRYPT_ENABLED=true and
TRAEFIK_PROTOCOL=https both need to be set, DNS must already resolve, and port
443 must be reachable from the internet for the TLS challenge. Check
docker compose logs --tail 100 traefik.
Links and emails point to localhost. ROOT_URL still has the default.
Set it to your public HTTPS address and recreate the stack.
To follow the app's logs live while you reproduce a problem:
cd ~/rocketchat-compose
docker compose logs -f rocketchat
Verification + next steps
You're done when https://chat.example.com loads over a valid certificate, the
wizard is complete and you're logged in as admin, ss shows 3000, 9458 and
27017 on loopback only, and a dated backup archive exists somewhere other
than this server.
From there, configure SMTP in the admin settings so invites and password resets work, then install the mobile apps and sign in to your server URL. If you want single sign-on, pair it with Keycloak or Authentik. For hosting picks sized for workloads like this, see Best VPS for Self-Hosting and Best VPS for Docker.