Overview
Jill Stingray is designed to run in containerized environments using Docker. This guide covers deployment to production, including environment setup, Docker configuration, and hosting recommendations.Prerequisites
- Docker and Docker Compose (or container hosting platform)
- PostgreSQL database (local or hosted)
- Discord bot application and token
- Supabase project (optional, for role icons)
- Node.js 24+ (for local development)
Environment Variables
Create a.env file in the project root with the following variables:
Required Variables
| Variable | Description | Example |
|---|---|---|
DISCORD_TOKEN | Bot token from Discord Developer Portal | MTIzNDU2Nzg5MDEyMzQ1Njc4OQ.GaBcDe.FgHiJk... |
DATABASE_URL | PostgreSQL connection string | postgresql://user:pass@host:5432/db |
Optional Variables
| Variable | Description | Default |
|---|---|---|
SUPABASE_URL | Supabase project URL | None (file uploads disabled) |
SUPABASE_SERVICE_KEY | Service role key (bypasses RLS) | Falls back to SUPABASE_KEY |
SUPABASE_KEY | Anon key (requires open RLS) | None |
PORT | HTTP keep-alive server port | 8080 |
Docker Deployment
Dockerfile Overview
The includedDockerfile uses Node.js 24 Alpine for minimal image size:
Why These Dependencies?
- python3, make, g++: Required for building native Node.js modules
- cairo-dev, pango-dev, etc.: Required by
@napi-rs/canvasfor image generation (used in/palette,/geo, etc.) - fontconfig: Font rendering support
Build the Image
Run with Docker
7860- Primary health check server8080- Secondary health check server (configurable viaPORT)
Docker Compose
Createdocker-compose.yml:
DATABASE_URL to:
Database Setup
Using External PostgreSQL
If using a hosted database (Supabase, Railway, Neon, etc.):- Create a PostgreSQL database
- Copy the connection string
- Add to
.envasDATABASE_URL - Ensure SSL is enabled (the bot uses
ssl: { rejectUnauthorized: false })
utils/db.js::init().
Using Docker Compose
The database container creates an empty PostgreSQL instance. Tables are created automatically when the bot starts.Manual Schema Creation (Optional)
If you need to create tables manually, extract the schema fromutils/db.js:12-127.
Health Checks
The bot runs two HTTP servers for health monitoring:Primary Server (Port 7860)
Defined inindex.js:10-17:
Secondary Server (Port 8080)
Defined inindex.js:100-105:
Testing Health Checks
Hosting Platforms
Railway
- Create new project from GitHub repo
- Add PostgreSQL plugin
- Set environment variables in Railway dashboard
- Railway auto-detects the Dockerfile
Render
- Create new Web Service
- Connect GitHub repository
- Set Docker as environment
- Add PostgreSQL database (separate service)
- Link database and set environment variables
Fly.io
- Install Fly CLI:
brew install flyctl - Login:
fly auth login - Launch:
fly launch - Set secrets:
fly secrets set DISCORD_TOKEN=... - Attach Postgres:
fly postgres createandfly postgres attach
Google Cloud Run
- Build image:
gcloud builds submit --tag gcr.io/PROJECT_ID/jill-stingray - Deploy:
gcloud run deploy jill-stingray --image gcr.io/PROJECT_ID/jill-stingray - Set environment variables in Cloud Run console
- Use Cloud SQL for PostgreSQL
Self-Hosted (VPS)
On Ubuntu/Debian server:Production Checklist
- Create Discord bot application
- Enable all required intents (Server Members, Message Content)
- Set up PostgreSQL database
- Configure environment variables
- Set up Supabase (optional, for role icons)
- Build and test Docker image locally
- Deploy to hosting platform
- Verify health check endpoints respond
- Invite bot to Discord server
- Test slash command registration
- Monitor logs for errors
Monitoring & Logs
Docker Logs
Startup Logs
Successful startup shows:Error Logs
The crash handler logs unhandled errors:Updating the Bot
Docker Compose
Docker (Manual)
Zero-Downtime Updates
For production, use:Scaling Considerations
Horizontal Scaling
Discord bots cannot be horizontally scaled (multiple instances) without complex sharding:- Use vertical scaling (more CPU/RAM)
- Optimize database queries
- Use caching for frequently accessed data
Database Optimization
- Enable connection pooling (already configured)
- Add indexes for frequently queried columns
- Use
EXPLAIN ANALYZEto optimize slow queries - Consider read replicas for analytics queries
Memory Management
The bot uses:- In-memory caches (
bot.settingsCache,bot.pendingActions) - Cached member data (loaded on
readyevent)
Troubleshooting
Bot Not Connecting
Check:DISCORD_TOKENis correct- Bot has required intents enabled in Discord Developer Portal
- Network connectivity to Discord API
Database Connection Failed
Check:DATABASE_URLformat is correct- Database server is reachable
- SSL settings match database requirements
Commands Not Appearing
Check:- Slash commands are synced (look for ”✅ Successfully synced” in logs)
- Bot has
applications.commandsscope - Wait up to 1 hour for global command sync
Role Icons Not Working
Check:- Server has Level 2 boost (required for role icons)
SUPABASE_SERVICE_KEYis set (not justSUPABASE_KEY)- RLS policies allow uploads to
role-iconsbucket
Security Best Practices
- Never commit
.envfiles to version control - Use secrets management in production (Railway secrets, Docker secrets, etc.)
- Rotate tokens periodically
- Use environment-specific tokens (dev vs. prod)
- Enable 2FA on Discord developer account
- Restrict database access to bot’s IP range
- Use read-only database users where possible
- Keep dependencies updated (
npm audit fix)
Backup Strategy
Database Backups
With Docker Compose:Configuration Backups
- Keep
.env.examplein repository (without secrets) - Document custom guild settings
- Export trigger keywords before major updates
Next Steps
- Architecture - Understand the codebase
- Database Schema - Learn about data structures
- Commands - Explore available commands