Environment Variables (.env)
A super-detailed reference for every variable in the .env file — what it does, where to get it, and when it's required.
The .env file stores all sensitive credentials and infrastructure settings for Kythia. It is loaded once at startup. You get this file by copying example.env and renaming it to .env.
.env to Git. It contains your Discord bot token, database password, and API secrets. It is already in .gitignore by default — do not remove it from there.🚀 Quick Start (3 Required Fields)
These are the only variables you need to get Kythia running. Everything else is optional.
LICENSE_KEY
| Required | ✅ Yes |
| Type | string |
Your unique Kythia license key. The bot refuses to start without a valid key.
How to get it: Join the Kythia Discord server and follow the instructions in the #license channel.
LICENSE_KEY="KYTHIA-XXXX-XXXX-XXXX-XXXX"
DISCORD_BOT_TOKEN
| Required | ✅ Yes |
| Type | string |
The secret token your bot uses to authenticate with Discord. This is the most sensitive value in the file — treat it like a password.
How to get it:
DISCORD_BOT_TOKEN="MTA4OT..."
DISCORD_BOT_CLIENT_ID
| Required | ✅ Yes |
| Type | string (numeric ID) |
The Application ID (also called Client ID) of your bot. Used for deploying slash commands and generating the invite link.
How to get it:
DISCORD_BOT_CLIENT_ID="1234567890123456789"
📦 Database Configuration
By default, Kythia uses SQLite — a file-based database that requires zero setup. The file (kythia.sqlite) is created automatically in your project root on first start.
DB_DRIVER
| Required | No (defaults to sqlite) |
The database engine to use.
| Value | Database | When to use |
|---|---|---|
sqlite |
SQLite | ✅ Default. Great for small/personal bots. No server needed. |
mysql |
MySQL 8+ | Recommended for production multi-shard bots |
mariadb |
MariaDB 10.3+ | Drop-in MySQL replacement |
postgres |
PostgreSQL 12+ | Alternative to MySQL |
mssql |
Microsoft SQL Server | Enterprise environments |
Before switching to MySQL/MariaDB/Postgres, you need to install the driver:
npm install mysql2
npm install mariadb
npm install pg pg-hstore
npm install tedious
DB_HOST
| Required | Only for MySQL / MariaDB / Postgres / MSSQL |
The hostname or IP address of your database server.
DB_HOST=localhost # Local machine
DB_HOST=123.45.67.89 # Remote VPS by IP
DB_HOST=db.example.com # Remote VPS by hostname
DB_PORT
| Required | Only for MySQL / MariaDB / Postgres / MSSQL |
The port your database server listens on.
| Database | Default Port |
|---|---|
| MySQL / MariaDB | 3306 |
| PostgreSQL | 5432 |
| MSSQL | 1433 |
DB_PORT=3306
DB_NAME
| Required | Only for MySQL / MariaDB / Postgres / MSSQL |
The name of the database Kythia should connect to. You need to create this before starting the bot.
-- MySQL / MariaDB
CREATE DATABASE kythia;
-- PostgreSQL
CREATE DATABASE kythia;
Fill in the database name in the .env file:
DB_NAME=kythia
DB_USER / DB_PASSWORD
| Required | Only for MySQL / MariaDB / Postgres / MSSQL |
The database user credentials. The user must have full SELECT, INSERT, UPDATE, DELETE, and CREATE permissions on the Kythia database.
DB_USER=root
DB_PASSWORD=your_secure_password
DB_STORAGE_PATH (SQLite only)
| Required | No |
Custom file path for the SQLite database file. If not set, defaults to kythia.sqlite in the project root.
DB_STORAGE_PATH=data/kythia.sqlite
DB_SOCKET_PATH (MySQL / MariaDB only)
| Required | No |
For managed cloud databases (e.g. Google Cloud SQL), use a Unix socket path instead of TCP DB_HOST. If both are set, socket takes priority.
DB_SOCKET_PATH=/cloudsql/project-id:region:instance-name
DB_DIALECT_OPTIONS (MSSQL only)
| Required | No |
Extra Sequelize dialect options passed as a JSON string. Usually needed for Azure or encrypted MSSQL connections.
DB_DIALECT_OPTIONS='{"options":{"encrypt":true,"trustServerCertificate":true}}'
⚡ Redis Cache
REDIS_URLS
| Required | No (but strongly recommended) |
| Format | Comma-separated Redis URLs |
Redis is used as Kythia's L2 cache layer, dramatically reducing database queries. Without it, Kythia falls back to in-memory caching (not shared between shards, lost on restart).
How to set up Redis:
sudo apt install redis-server
sudo systemctl enable --now redis
# Test it:
redis-cli ping # Should return PONG
docker run -d --name redis -p 6379:6379 redis:latest
# Single instance
REDIS_URLS="redis://localhost:6379"
# With password
REDIS_URLS="redis://:your_password@localhost:6379"
# Multiple nodes (cluster/redundancy)
REDIS_URLS="redis://node1:6379,redis://node2:6379"
🔧 Additional Discord Settings
DISCORD_BOT_CLIENT_SECRET
| Required | Only if using the web dashboard |
The Client Secret from your Discord application. Used for OAuth2 login in the web dashboard. Different from the bot token.
How to get it:
DISCORD_BOT_CLIENT_SECRET="dQw4w9WgXcQ..."
📊 Monitoring & Logging
SENTRY_DSN
| Required | No |
Enables automatic error tracking via Sentry. When set, all unhandled exceptions are captured and reported with full stack traces.
How to get it:
https://xxxx@o123.ingest.sentry.io/123)SENTRY_DSN="https://xxxx@o123456.ingest.sentry.io/123456"
WEBHOOK_GUILD_INVITE_LEAVE
| Required | No |
A Discord webhook URL. Kythia posts a notification whenever the bot joins or leaves a server — useful for monitoring bot growth.
How to create a webhook:
WEBHOOK_GUILD_INVITE_LEAVE="https://discord.com/api/webhooks/..."
WEBHOOK_ERROR_LOGS
| Required | No |
A Discord webhook URL. Kythia posts critical unhandled errors to this channel. Pair with settings.webhookErrorLogs: true in kythia.config.js to activate.
WEBHOOK_ERROR_LOGS="https://discord.com/api/webhooks/..."
🎵 Music Addon
LAVALINK_HOSTS / LAVALINK_PORTS / LAVALINK_PASSWORDS / LAVALINK_SECURES
| Required | Only if music.active = true |
| Format | Comma-separated (one value per node) |
All four variables must have the same number of comma-separated items. Each index corresponds to one Lavalink node.
Required Lavalink plugins:
lavasrc-plugin— Spotify, Apple Music, Deezeryoutube-plugin— YouTube (with yt-cipher)lavasearch-plugin— Autocomplete searchsponsorblock-plugin— Skip sponsor segments
Use Lavalink version 4.1.1 for full compatibility.
# Single node
LAVALINK_HOSTS="localhost"
LAVALINK_PORTS="2333"
LAVALINK_PASSWORDS="youshallnotpass"
LAVALINK_SECURES="false" # "true" if node uses HTTPS/WSS
# Two nodes (load balanced)
LAVALINK_HOSTS="node1.example.com,node2.example.com"
LAVALINK_PORTS="2333,443"
LAVALINK_PASSWORDS="secret1,secret2"
LAVALINK_SECURES="false,true"
SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET
| Required | No (Spotify links won't resolve without it) |
Enables Spotify track support. Requires lavasrc-plugin on your Lavalink server.
How to get Spotify credentials:
SPOTIFY_CLIENT_ID="your_client_id"
SPOTIFY_CLIENT_SECRET="your_client_secret"
AUDD_API_KEY
| Required | No |
API key for Audd.io — a music recognition service that improves lyrics detection accuracy in the music addon.
How to get it: Sign up at audd.io and grab your API key from the dashboard. Free tier available.
AUDD_API_KEY="your_audd_key"
🤖 AI Addon
GEMINI_API_KEYS
| Required | Only if ai.active = true |
| Format | Comma-separated API keys |
Google Gemini API keys for the AI chat feature. Multiple keys are used in round-robin rotation to spread requests and multiply your free rate limit.
How to get Gemini API keys:
# Single key
GEMINI_API_KEYS="AIzaSyXXXXXXXX"
# Multiple keys (recommended — round-robin load balanced)
GEMINI_API_KEYS="AIzaSyXXXX,AIzaSyYYYY,AIzaSyZZZZ"
🌐 Dashboard / API Addon
API_SECRET
| Required | Only if api.active = true |
| Recommended length | 32+ characters |
A long random string used to sign and verify session cookies for the dashboard. Changing this will log out all active users.
Generate a strong secret:
# Linux/macOS terminal:
openssl rand -hex 32
API_SECRET="a8f3c9d2e1b4a7f6c3e2d1b0a9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0"
API_URL
| Required | Only if api.active = true |
The full public URL where the dashboard is accessible. This is sent as the redirect_uri in the Discord OAuth2 flow — it must exactly match a redirect URI listed in your Discord Developer Portal.
After setting this, go to Discord Developer Portal → Your App → OAuth2 → Redirects and add {API_URL}/auth/discord/callback as an allowed redirect.
Example: If API_URL=https://panel.kythia.my.id, add https://panel.kythia.my.id/auth/discord/callback
# Local development
API_URL="http://localhost:3000"
# VPS with raw IP
API_URL="http://123.45.67.89:3000"
# Custom domain with HTTPS
API_URL="https://panel.kythia.my.id"
API_PORT
| Required | No |
| Default | 3000 |
The port the internal Express API server listens on. Make sure this port is open in your firewall/VPS security group.
API_PORT=3000
🗳️ Top.gg Integration
TOPGG_API_KEY
| Required | Only if using vote-locked features |
Your Top.gg API key. Used to check whether a specific user has voted for your bot.
How to get it:
TOPGG_API_KEY="your_topgg_api_key"
TOPGG_AUTH_TOKEN
| Required | Only if using vote webhooks |
The authorization token you set in Top.gg's webhook configuration. Kythia uses this to verify that incoming vote webhook requests are legitimate (not faked).
How to set it up:
https://your-domain.com/api/webhooks/topggTOPGG_AUTH_TOKEN hereTOPGG_AUTH_TOKEN="a_random_secret_you_make_up"
WEBHOOK_VOTE_LOGS
| Required | No |
A Discord webhook URL. Kythia sends a message here whenever a user votes for the bot on Top.gg.
WEBHOOK_VOTE_LOGS="https://discord.com/api/webhooks/..."
🛠️ Utility Addons
EXCHANGERATE_API
| Required | No |
API key for ExchangeRate-API — used by the /currency command in the Core addon for real-time currency conversion.
How to get it:
EXCHANGERATE_API="your_exchangerate_api_key"
KYTHIA_IMAGE_STORAGE_URL / KYTHIA_IMAGE_STROAGE_API_KEY
| Required | Only if image.active = true |
URL and API key for your self-hosted Kythia Storage Server. The image addon uses this to store and serve user-uploaded images.
STROAGE (not STORAGE) is intentional and must match exactly in your .env.KYTHIA_IMAGE_STORAGE_URL="https://storage.example.com"
KYTHIA_IMAGE_STROAGE_API_KEY="your_storage_api_key"
☁️ Pro Addon (Cloudflare Subdomains)
CLOUDFLARE_API_TOKEN
| Required | Only if pro.active = true |
A scoped Cloudflare API token with DNS:Edit permission. This is not your Global API Key — never use the global key; always create a scoped token.
How to create a scoped token:
CLOUDFLARE_API_TOKEN="your_scoped_dns_edit_token"
CLOUDFLARE_ZONE_ID
| Required | Only if pro.active = true |
The Zone ID identifies your specific domain in Cloudflare.
How to find it:
CLOUDFLARE_ZONE_ID="abc123def456..."
CLOUDFLARE_DOMAIN
| Required | Only if pro.active = true |
Your root domain. Users will receive subdomains like username.yourdomain.com.
CLOUDFLARE_DOMAIN="kyth.me"
🔗 Internal Features (Official Only)
| Variable | Purpose |
|---|---|
GLOBAL_CHAT_API_URL |
Base URL of the Global Chat API |
GLOBAL_CHAT_API_KEY |
Authentication key for Global Chat |
GLOBAL_VOICE_API_URL |
Base URL of the Global Voice API |
GLOBAL_VOICE_API_KEY |
Authentication key for Global Voice |
GLOBAL_CHAT_API_URL=""
GLOBAL_CHAT_API_KEY=""
GLOBAL_VOICE_API_URL=""
GLOBAL_VOICE_API_KEY=""