Skip to content
FluffBoost

Configuration

Every environment variable, validated by Zod at startup.

All configuration lives in environment variables, validated by a Zod schema in apps/discord/src/utils/envSchema.ts. Invalid config exits the process immediately — a missing or malformed value fails fast instead of misbehaving later.

Locally these come from apps/discord/.env. In production, inject them through your platform (see Deployment).

Required

VariableNotes
DATABASE_URLPostgreSQL connection string
REDIS_URLRedis connection string
DISCORD_APPLICATION_BOT_TOKENBot token
OWNER_IDSnowflake of the bot owner
MAIN_CHANNEL_IDAnnouncement channel snowflake

Optional (with defaults)

VariableDefaultNotes
DATABASE_POOL_MAX10Max Postgres pool size per process (1–100)
DATABASE_QUERY_LOGfalseLogs SQL query text (never parameters) at debug level. Development only
WORKER_CONCURRENCY4BullMQ worker concurrency per shard
DISCORD_DEFAULT_STATUSSpreading Paw-sitivity 🐾Fallback presence text
DISCORD_DEFAULT_ACTIVITY_TYPECustomPlaying, Streaming, Listening, Custom
DEFAULT_ACTIVITY_URL—Stream URL (for Streaming)
DISCORD_ACTIVITY_INTERVAL_MINUTES15Presence rotation interval (1–1440)
ALLOWED_USERS—Comma-separated snowflakes allowed to run /admin. Empty disables every /admin command
HOST— (all interfaces)Health API bind address. Leave unset in Docker/Dokploy; 127.0.0.1 binds loopback only
PORT3000Health API port, a number from 1 to 65535
VERSIONapps/discord/package.json versionShown in /about and /changelog
NODE_ENVdevelopmentdevelopment, production, test
PREMIUM_ENABLEDfalsePremium gating. false opens custom schedules to every server (see below)
DISCORD_PREMIUM_SKU_ID—Required only when PREMIUM_ENABLED=true; an empty value counts as unset

SQL is not logged by default, including in development. Set DATABASE_QUERY_LOG=true to see query text while debugging.

What PREMIUM_ENABLED=false means

false turns Premium gating off; it does not hide custom scheduling. /setup schedule is open to every server, saved custom schedules apply, and there is no purchase button or entitlement handling. That suits a self-hosted bot. A bot that sells Premium must set PREMIUM_ENABLED=true and DISCORD_PREMIUM_SKU_ID; see Enable Premium.

Container-only settings

SKIP_MIGRATIONS=true is read by the Docker entrypoint, not by the bot, and skips the startup migration step. Use it only while you baseline or reconcile an existing database; with no public."Guild" table the bot exits with "Database schema missing" instead of starting; see Database & schema for how migrations run.

Postgres connection budget

DATABASE_POOL_MAX applies per process. The supervisor process and every shard process each open their own pool (connections are opened lazily and closed after 30 seconds idle; the supervisor only needs one or two). Keep:

shards × DATABASE_POOL_MAX + 2  ≤  max_connections − reserved connections

Discord's recommended shard count grows with the number of servers, so lower DATABASE_POOL_MAX (or add a pooler such as PgBouncer) as shards are added. Run PgBouncer in session mode: transaction mode needs prepared statements turned off in the client, which the bot does not do, and the migration step holds a session-level advisory lock.

Optional and unused

The bot does not read these. They are accepted (and validated as snowflakes when set) so older .env files keep working, and can be removed.

VariableNotes
DISCORD_APPLICATION_IDThe bot uses client.application instead
DISCORD_APPLICATION_PUBLIC_KEYOnly needed for HTTP interactions, which the bot does not use
MAIN_GUILD_IDAnnouncements use MAIN_CHANNEL_ID alone

If PREMIUM_ENABLED=true, DISCORD_PREMIUM_SKU_ID must be set — the schema rejects the config otherwise.

The current template always lives in apps/discord/.env.example.

On this page