The Discord Faceit Hub Bot is a Node.js application that runs a Discord client and an Express HTTP server in the same process. It needs a persistent, publicly accessible server so Faceit can deliver webhooks over HTTPS. This guide covers deploying on a VPS with PM2 and Nginx, as well as a Docker approach.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/VasquezRivero92/Discord_Faceit/llms.txt
Use this file to discover all available pages before exploring further.
Requirements
- Node.js 18+ — the bot uses ES modules (
"type": "module") and top-levelawait - A public domain or IP with HTTPS — Faceit requires HTTPS for webhook delivery
- A Firebase/Firestore project — all persistent state (users, matches, config) lives in Firestore
- Discord application with a bot token, OAuth2 client ID and secret
Direct VPS Deployment
Configure Environment Variables
Copy the example env file and fill in every value:Minimum required variables:
Set Up PM2 for Process Management
Install PM2 globally, start the bot, and configure it to survive reboots:Follow the output of
pm2 startup — it prints a system-specific command to run as root to register the PM2 service. After running it, your bot will automatically restart on server reboot.Useful PM2 commands:Nginx Reverse Proxy
Place Nginx in front of the Node.js server to handle SSL termination and forward requests to port 3000. The WebSocket upgrade headers are required for the Mixton Arena real-time panel (/api/lobby/mixton-ws).
app.set('trust proxy', 1) so it correctly reads the client IP from X-Forwarded-For instead of seeing Nginx’s loopback address. This is needed for rate limiting to work correctly per real client IP.
SSL/TLS with Let’s Encrypt
Faceit requires HTTPS for webhook delivery. Use Certbot to obtain a free certificate:listen 443 ssl and sets up auto-renewal. After this step, set BASE_URL=https://your-domain.com in your .env.
The Express app automatically enables secure: true on session cookies when BASE_URL starts with https:
Docker Deployment
For containerized environments, use this minimal Dockerfile:Environment Variables Reference
| Variable | Required | Description |
|---|---|---|
DISCORD_TOKEN | ✅ | Bot token from Discord Developer Portal |
DISCORD_CLIENT_ID | ✅ | Application client ID for OAuth2 |
DISCORD_CLIENT_SECRET | ✅ | Application client secret for OAuth2 |
DISCORD_GUILD_ID | ✅ | Default Discord guild ID |
ADMIN_JWT_SECRET | ✅ | Secret used to sign admin session JWTs |
FACEIT_API_KEY | ✅ | Faceit server-side API key |
FACEIT_HUB_ID | ✅ | Your Faceit Hub UUID |
FACEIT_LEADERBOARD_ID | — | Leaderboard ID for /leaderboard command |
FACEIT_WEBHOOK_SECRET | — | Webhook signature secret (required if webhookValidation is enabled) |
FIREBASE_SERVICE_ACCOUNT_PATH | — | Path to Firebase service account JSON file |
BASE_URL | ✅ | Public URL of your server (e.g. https://your-domain.com) |
PORT | — | HTTP listen port (default: 3000) |
NODE_ENV | — | Set to production to enable secure cookies |
ADMIN_ROLE_NAME | — | Discord role name for admins (default: ADMIN) |
Graceful Shutdown
The bot handlesSIGTERM and SIGINT to shut down cleanly — disconnecting the Discord client and closing the Express server before exiting. PM2 sends SIGTERM on pm2 restart and pm2 stop, so in-flight requests complete before the process exits.
Rate Limiting
Two rate limiters are applied usingexpress-rate-limit with standardHeaders: true:
| Scope | Limit | Window |
|---|---|---|
| Global (all routes) | 100 requests | 1 minute |
/webhook/faceit | 60 requests | 1 minute |
app.set('trust proxy', 1), rate limiting is keyed on the real client IP from X-Forwarded-For, not the Nginx loopback IP.
Health Check
Monitor the bot’s status with the health endpoint — no authentication required:GET /health every 60 seconds and alert if the response is not 200 or if discord is not "connected".