Rocket.Chat on AlmaLinux 10: Docker or Bust?

The “Easy” Install is a Lie

I have a confession. Last Tuesday, I spent three hours debugging a Snap package that decided to auto-update and break its own permissions. It wasn’t even a complex setup—just a standard chat server for a dev team. But that’s the thing about “easy” deployment methods like Snaps or one-line install scripts: they work perfectly until the exact moment you need to sleep.

So when I spun up a fresh AlmaLinux 10 instance this morning to deploy Rocket.Chat, I looked at the documentation suggesting the Snap method and laughed. No thanks.

If you’re running Enterprise Linux—whether it’s RHEL, Alma, or Rocky—you’re probably doing it because you want stability. You want control. Smashing a black-box container manager like Snapd on top of a pristine EL10 system feels wrong. It’s like putting ketchup on a wagyu steak.

Today, I’m walking through how I actually deploy this stuff in production. No magic scripts. Just Docker, a bit of Nginx, and enough SELinux configuration to keep security happy without making me cry.

Why Docker Wins (Even in 2026)

I used to be a “compile from source” purist. I really did. There was something satisfying about extracting a tarball, setting up the systemd service manually, and knowing exactly where every binary lived. But Rocket.Chat—and modern Node.js apps in general—have dependencies that are just painful to manage manually on an Enterprise OS.

Node versions drift. MongoDB versions drift. Trying to match the exact Node.js version Rocket.Chat needs with what’s available in the AlmaLinux 10 AppStream is a losing battle. You’ll end up compiling Node from source, and nobody has time for that.

So, we use Docker. It isolates the chaos.

The Setup

First off, get the basics ready. If you’re on a fresh minimal install of AlmaLinux 10, you probably don’t even have tar installed (okay, maybe you do, but I’ve seen minimal images that are too minimal).

Rocket.Chat logo - Rocket.chat Logo PNG Vector (AI, PDF, SVG) Free Download
Rocket.Chat logo – Rocket.chat Logo PNG Vector (AI, PDF, SVG) Free Download
# Update everything first. Always.
sudo dnf update -y

# Install the utils we actually need
sudo dnf install -y yum-utils device-mapper-persistent-data lvm2

# Add the Docker repo (yes, the standard one usually works fine on EL10)
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# Install the engine
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Fire it up
sudo systemctl start docker
sudo systemctl enable docker

Side note: I still type docker-compose out of muscle memory, even though it’s docker compose (with a space) now. Old habits die hard.

The MongoDB Replica Set Headache

Here is where 90% of people get stuck. Rocket.Chat requires a MongoDB Replica Set. It’s not optional. It uses the Oplog for real-time message syncing. If you just spin up a standalone Mongo container, Rocket.Chat will crash, loop, and fill your logs with sadness.

You need a compose file that orchestrates this. I’ve tweaked this one over the last few years to be as “set and forget” as possible.

version: '3.8'

services:
  rocketchat:
    image: registry.rocket.chat/rocketchat/rocket.chat:latest
    command: >
      bash -c "for i in seq 1 30; do
        node main.js &&
        s=$$? && break || s=$$?;
        echo \"Tried $$i times. Waiting 5 secs...\";
        sleep 5;
      done; (exit $$s)"
    restart: unless-stopped
    volumes:
      - ./uploads:/app/uploads
    environment:
      - PORT=3000
      - ROOT_URL=https://chat.yourdomain.com
      - MONGO_URL=mongodb://mongo:27017/rocketchat?replicaSet=rs0&directConnection=true
      - MONGO_OPLOG_URL=mongodb://mongo:27017/local?replicaSet=rs0&directConnection=true
    depends_on:
      - mongo
    ports:
      - "3000:3000"

  mongo:
    image: mongo:7.0
    restart: unless-stopped
    volumes:
      - ./data/db:/data/db
      - ./data/dump:/dump
    command: mongod --oplogSize 128 --replSet rs0 --bind_ip_all
    ports:
      - "27017:27017"

  # This little guy initializes the replica set so you don't have to manually shell in
  mongo-init-replica:
    image: mongo:7.0
    command: >
      bash -c "for i in seq 1 30; do
          mongosh mongo/rocketchat --eval \"
            rs.initiate({
              _id: 'rs0',
              members: [ { _id: 0, host: 'localhost:27017' } ]})\" &&
          s=$$? && break || s=$$?;
          echo \"Tried $$i times. Waiting 5 secs...\";
          sleep 5;
        done; (exit $$s)"
    depends_on:
      - mongo

See that mongo-init-replica service? That saved my sanity. Without it, you have to manually exec into the Mongo container and run rs.initiate(). Why do manual labor when a script can do it?

The “Enterprise” Part: SELinux and Firewalls

If you just run docker compose up -d now, it might work. Or, if you’re on a hardened AlmaLinux install, it might fail silently because SELinux hates your bind mounts.

I see so many tutorials say “just run setenforce 0“.

Don’t do that.

Disabling SELinux is like taking the batteries out of your smoke detector because you burned toast once. Instead, just label your volumes correctly. If you keep your data in /opt/rocketchat, tell SELinux it’s okay.

# Make the directory
sudo mkdir -p /opt/rocketchat/data/db
sudo mkdir -p /opt/rocketchat/uploads

# Set the context. The :z option in Docker usually handles this, 
# but sometimes on EL10 you need to be explicit if you're using host paths.
sudo chcon -Rt svirt_sandbox_file_t /opt/rocketchat/

And for the firewall? Firewalld is standard on Alma. Don’t install iptables-services unless you enjoy pain.

# Allow HTTP/HTTPS
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

Reverse Proxy: Nginx vs Caddy

Rocket.Chat logo - The Square - How do I quote a direct message on Rocket.Chat?
Rocket.Chat logo – The Square – How do I quote a direct message on Rocket.Chat?

I’m torn here. Caddy is magical—it handles SSL certificates automatically and the config is three lines long. But Nginx is the devil I know.

For a quick internal tool, I use Caddy. For something facing the public internet where I need strict rate limiting and specific header manipulation? Nginx.

Let’s assume you want Nginx because you’re a control freak like me. You can run this in a container too, but on the host OS, it feels more robust for handling SSL termination.

server {
    listen 443 ssl http2;
    server_name chat.yourdomain.com;

    # SSL cert paths here...
    
    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header X-Nginx-Proxy true;
        proxy_redirect off;
    }
}

The Upgrade and Connection headers are critical. Rocket.Chat relies heavily on WebSockets. If you miss those lines, your users will get stuck in a “Connecting…” loop that never ends, and they will email you. Repeatedly.

When Things Break (Because They Will)

Even with Docker, things go sideways. The most common issue I see in 2026 is still the Oplog tailing issue. If your Mongo container restarts uncleanly, sometimes the lock file persists.

Docker logo - How Docker works and what are its secrets | Cooltechzone
Docker logo – How Docker works and what are its secrets | Cooltechzone

If Rocket.Chat is stuck in a restart loop, check the logs:

docker compose logs -f rocketchat

If you see MongoNetworkError, check if your mongo container is actually healthy. Sometimes the mongo-init-replica script runs too fast, fails, and exits. I added a retry loop in the compose file above specifically to handle this race condition.

Another thing: RAM. Java used to be the memory hog, but Node.js is giving it a run for its money. On a small VPS (like 2GB RAM), the build process or initial startup might OOM (Out of Memory). If your container exits with code 137, that’s the Linux OOM killer taking a shot. Give it some swap space or upgrade the instance.

Final Thoughts

Is this the absolute fastest way to install Rocket.Chat? No. The fastest way is snap install rocketchat-server.

But fast isn’t always good. When that Snap package updates automatically on a Friday night and breaks compatibility with your custom integrations, “fast” won’t comfort you.

By using Docker on AlmaLinux 10, we get the stability of an Enterprise kernel with the flexibility of containerized dependencies. It takes twenty minutes to set up instead of two, but those eighteen minutes buy you peace of mind. And frankly, I’d rather spend my time configuring firewalls than fighting with Snap permissions.

Can Not Find Kubeconfig File