Skip to content

Instantly share code, notes, and snippets.

@zicklag
Last active May 17, 2024 19:01
Show Gist options
  • Save zicklag/c5adbf9bf4c5adb7640ed3e752c9eb28 to your computer and use it in GitHub Desktop.
Save zicklag/c5adbf9bf4c5adb7640ed3e752c9eb28 to your computer and use it in GitHub Desktop.
Self-hosted Revolt Chat Server With Traefik Reverse Proxy.

Self-hosting Revolt

This is my ultra-quick, rundown of how I self hosted revolt. It may not be 100% accurate, I might mess something up, and it might not work, but it might help out.

I've also got some personal server-setup preferences in here, so you'll get a couple bonuses, like a Portainer web UI for managing your Docker stacks, and a backup system. Feel free to ignore those/leave them out.

Step 1: Get a Server

There are lots of different places to get servers, so I'm going to gloss over this step, other than an important piece:

Step 1.a: Open up your firewall ports

Lots of cloud providers automatically block lots of ports though a firewall that can be configured in the UI. Usually port 22 ( SSH ) is already allowed. You need to make sure that port 80 and port 443 are also allowed.

Step 2: Setup a Domain

You need to register a domain with some domain provider, which is out of scope for this tutorial, except for the next important step:

Step 2.a: Point a Wildcard Domain to your Server

You need to create a new DNS record that points *.yourwebsite.com to your server's IP address. Exact process is out of scope.

Step 3: Install Docker On Your Server

See the Docker Documentation.

Step 4: Setup Your Docker "Core Stack"

The "core stack" is what I call the first docker stack we deploy. We will put our Traefik load balancer in this core stack. We keep it separate from our Revolt stack because Traefik can be useful when we have any number of different apps hosted on our server, and it won't really need to change much.

First off, make sure you're logged in as root on your server and create some folders:

# Create folder for storing docker volumes
mkdir -p /docker-volumes/
chmod 700 /docker-volumes

# Create folder for storing our backups
mkdir -p /backups/
chmod 700 /backups

# Create the folder for the docker core stack
mkdir -p /docker-compose-core-stack/
chmod 744 /docker-compose-core-stack

Now we need to create the Docker network we will use for our reverse proxy.

docker network create webgateway

Create copy this file to /docker-compose-core-stack/docker-compose.yml.

And copy and modify this file to /docker-compose-core-stack/.env.

Modify the RESTIC_PASSWORD and DOMAIN variables in that .env file to match your domain and desired password.

Also run this command and fill out the password:

$ htpasswd -n myusername
myusername:$apr1$tB7jbznW$IH0wShSpEJ6xUJnKL4JjQ.

And copy that and put it in between the quotes after TRAEFIK_USERS. That is the password to your traefik dashboard ( which you might not need, but can be useful when routing goes bad.

Finally, make sure you're in /docker-compose-core-stack and run docker-compose up -d to start all the services.

Step 5: Log into Portainer

You should now be able to go to portainer.your-website.com, and walk through the process to create your user account for managing Docker through the portainer web interface.

Step 6: Deploy Revolt

In the portainer web UI, you can now go to stacks and click "Create Stack".

Paste this file in as the Docker Compose YAML.

Then add the following environment variables using the slot below the code block:

DOMAIN=chat.your-website.com
MINIO_ROOT_PASSWORD=securepassword
MINIO_ROOT_USER=admin
VAPID_PRIVATE_KEY=[insert your vapid private key]
VAPID_PUBLIC_KEY=[insert your vapid public key]

Now click the "Deploy" button at the bottom.

Step 7: Fix Anything Broken / Rest From Your Work

If that works great! Otherwise, you'll have to figure out what went wrong.

That's all the time I have for writing a guide, right now, but hopefully it helps.

A great bonus is that now you have a great server setup for hosting any other Docker apps that you want too. Just create another stack in the web UI and you can deploy all kinds of apps like a pro!

Disclaimer

THE ADVICE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ADVICE OR THE USE OR OTHER DEALINGS IN THE ADVICE.

DOMAIN=example.org
HOSTNAME=my-server
RESTIC_PASSWORD=password
TRAEFIK_USERS='user:$apr1$2Ced2Aqh$PbmZUNjzGgPM7gLRVMyUT0'
version: '3'
services:
traefik:
image: traefik:v2.8
restart: always
networks:
- webgateway
ports:
- "80:80"
- "443:443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./traefik-acme-data:/etc/traefik/acme
command:
# Enable dashboard
- --api.dashboard=true
# Entrypoints
- --entrypoints=Name:http Address::80 Redirect.EntryPoint:https
- --entrypoints=Name:https Address::443 TLS
- --entrypoints.web.address=:80
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.websecure.address=:443
- --certificatesresolvers.letsencrypt.acme.email=your@email.com
- --certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json
- --certificatesresolvers.letsencrypt.acme.tlschallenge=true
# Uncomment line below to use ACME staging dir
#- --certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --providers.docker=true
- --log.level=debug
- --providers.docker.network=webgateway
labels:
# Dashboard config
- traefik.http.routers.traefik-dash.rule=Host(`traefik.${DOMAIN}`)
- traefik.http.routers.traefik-dash.service=api@internal
- traefik.http.routers.traefik-dash.middlewares=traefik-dash-auth
- traefik.http.routers.traefik-dash.tls=true
- traefik.http.routers.traefik-dash.tls.certResolver=letsencrypt
- traefik.http.middlewares.traefik-dash-auth.basicauth.users=${TRAEFIK_USERS}
portainer:
image: portainer/portainer-ce:2.13.1-alpine
restart: always
networks:
- webgateway
volumes:
- ./portainer-data:/data
- /var/run/docker.sock:/var/run/docker.sock
labels:
- "traefik.http.routers.rtr-portainer.rule=Host(`portainer.${DOMAIN}`)"
- traefik.http.routers.rtr-portainer.tls=true
- traefik.http.routers.rtr-portainer.tls.certResolver=letsencrypt
- traefik.http.services.srv-portainer.loadbalancer.server.port=9000
restic:
image: lobaro/restic-backup-docker:1.2-0.9.4
restart: always
hostname: ${HOSTNAME}
environment:
RESTIC_REPOSITORY: /mnt/restic
BACKUP_CRON: 0 0 * * *
RESTIC_PASSWORD: ${RESTIC_PASSWORD}
RESTIC_FORGET_ARGS: >
--prune
--keep-daily 7
--keep-weekly 5
--keep-monthly 12
--keep-yearly 75
volumes:
- /docker-volumes:/data/docker-volumes
- ./portainer-data:/data/portainer-data
- /backups:/mnt/restic
networks:
webgateway:
external: true
version: '3.8'
services:
# MongoDB database
database:
image: mongo
networks:
- default
restart: always
environment: &env
- AUTUMN_MONGO_URI=mongodb://database
- MONGODB=mongodb://database
- REDIS_URI=redis://redis/
- REVOLT_APP_URL=https://${DOMAIN}
- REVOLT_PUBLIC_URL=https://api.${DOMAIN}
- VITE_API_URL=https://api.${DOMAIN}
- REVOLT_EXTERNAL_WS_URL=wss://events.${DOMAIN}
- AUTUMN_PUBLIC_URL=https://files.${DOMAIN}
- JANUARY_PUBLIC_URL=https://metadata.${DOMAIN}
- REVOLT_UNSAFE_NO_CAPTCHA=1
- REVOLT_UNSAFE_NO_EMAIL=1
- REVOLT_INVITE_ONLY=0
- REVOLT_MAX_GROUP_SIZE=150
- REVOLT_VAPID_PRIVATE_KEY=${VAPID_PRIVATE_KEY}
- REVOLT_VAPID_PUBLIC_KEY=${VAPID_PUBLIC_KEY}
- AUTUMN_S3_REGION=minio
- AUTUMN_S3_ENDPOINT=http://minio:9000
- MINIO_ROOT_USER=${MINIO_ROOT_USER}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
- AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER}
- AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
volumes:
- /docker-volumes/chat-example-org/db-data:/data/db
# Redis server
redis:
image: eqalpha/keydb
restart: always
# API server (delta)
api:
image: ghcr.io/revoltchat/server:20220715-1
networks:
- default
- webgateway
environment: *env
depends_on:
- database
- redis
restart: always
labels:
- traefik.docker.network=webgateway
- traefik.http.routers.rtr-revolt-api.rule=Host(`api.${DOMAIN}`)
- traefik.http.routers.rtr-revolt-api.tls=true
- traefik.http.routers.rtr-revolt-api.tls.certResolver=letsencrypt
- traefik.http.services.srv-revolt-api.loadbalancer.server.port=8000
# This middleware keeps people from creating new servers ( useful for single-server, self-hosted instances )
# Uncomment below three lines if you want to do that.
#- traefik.http.routers.rtr-revolt-api.middlewares=mdl-revolt-block-server-create
#- traefik.http.middlewares.mdl-revolt-block-server-create.replacepathregex.regex=^/servers/create(.*)
#- traefik.http.middlewares.mdl-revolt-block-server-create.replacepathregex.replacement=/servers/create/blocked_by_reverse_proxy
# Events service (quark)
events:
image: ghcr.io/revoltchat/bonfire:20220715-1
networks:
- default
- webgateway
environment: *env
depends_on:
- database
- redis
restart: always
labels:
- traefik.docker.network=webgateway
- traefik.http.routers.rtr-revolt-events.rule=Host(`events.${DOMAIN}`)
- traefik.http.routers.rtr-revolt-events.tls=true
- traefik.http.routers.rtr-revolt-events.tls.certResolver=letsencrypt
- traefik.http.services.srv-revolt-events.loadbalancer.server.port=9000
# Web App (revite)
web:
image: ghcr.io/revoltchat/client:master
networks:
- default
- webgateway
restart: always
environment: *env
labels:
- traefik.docker.network=webgateway
- traefik.http.routers.rtr-revolt-web.rule=Host(`${DOMAIN}`)
- traefik.http.routers.rtr-revolt-web.tls=true
- traefik.http.routers.rtr-revolt-web.tls.certResolver=letsencrypt
- traefik.http.services.srv-revolt-web.loadbalancer.server.port=5000
# S3-compatible storage server
minio:
image: minio/minio
command: server --console-address 0.0.0.0:8000 /data
networks:
- default
- webgateway
volumes:
- /docker-volumes/chat-example-org/minio:/data
restart: always
environment:
MINIO_BROWSER_REDIRECT_URL: http://console.s3.${DOMAIN}
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
labels:
- traefik.docker.network=webgateway
- traefik.http.routers.rtr-revolt-s3.rule=Host(`s3.${DOMAIN}`)
- traefik.http.routers.rtr-revolt-s3.tls=true
- traefik.http.routers.rtr-revolt-s3.tls.certResolver=letsencrypt
- traefik.http.routers.rtr-revolt-s3.service=srv-revolt-s3
- traefik.http.services.srv-revolt-s3.loadbalancer.server.port=9000
- traefik.http.routers.rtr-revolt-s3-console.rule=Host(`console.s3.${DOMAIN}`)
- traefik.http.routers.rtr-revolt-s3-console.tls=true
- traefik.http.routers.rtr-revolt-s3-console.tls.certResolver=letsencrypt
- traefik.http.routers.rtr-revolt-s3-console.service=srv-revolt-s3-console
- traefik.http.services.srv-revolt-s3-console.loadbalancer.server.port=8000
# Create buckets for minio.
createbuckets:
image: minio/mc
depends_on:
- minio
environment: *env
entrypoint: >
/bin/sh -c "
while ! curl -s --output /dev/null --connect-timeout 1 http://minio:9000; do echo 'Waiting minio...' && sleep 0.1; done;
/usr/bin/mc alias set minio http://minio:9000 ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD};
/usr/bin/mc mb minio/attachments;
/usr/bin/mc mb minio/avatars;
/usr/bin/mc mb minio/backgrounds;
/usr/bin/mc mb minio/icons;
/usr/bin/mc mb minio/banners;
/usr/bin/mc mb minio/emojis;
exit 0;
"
# File server (autumn)
autumn:
image: ghcr.io/revoltchat/autumn:1.1.5
networks:
- webgateway
- default
depends_on:
- database
- createbuckets
environment: *env
restart: always
labels:
- traefik.docker.network=webgateway
- traefik.http.routers.rtr-revolt-files.rule=Host(`files.${DOMAIN}`)
- traefik.http.routers.rtr-revolt-files.tls=true
- traefik.http.routers.rtr-revolt-files.tls.certResolver=letsencrypt
- traefik.http.services.srv-revolt-files.loadbalancer.server.port=3000
# Metadata and image proxy (january)
january:
image: ghcr.io/revoltchat/january:master
restart: always
environment: *env
labels:
- traefik.docker.network=webgateway
- traefik.http.routers.rtr-revolt-metadata.rule=Host(`metadata.${DOMAIN}`)
- traefik.http.routers.rtr-revolt-metadata.tls=true
- traefik.http.routers.rtr-revolt-metadata.tls.certResolver=letsencrypt
- traefik.http.services.srv-revolt-metadata.loadbalancer.server.port=7000
networks:
default:
webgateway:
external: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment