Keeping Your Discord Bot Token Safe with Environment Variables
How bot tokens leak, what an attacker can do with one, and a practical setup for keeping tokens out of your code, logs and git history.
On this page
A Discord bot token is a password that never expires on its own and bypasses two-factor authentication. Anyone who has it can log in as your bot and do everything its permissions allow — in every server it has joined. Protecting it is the single most important security task for a bot developer, and it’s mostly about habits.
What an attacker can do with your token
With a leaked token, someone can:
- Delete channels, roles and messages in every server your bot can manage.
- Ban or kick members, or mass-DM them with scam links.
- Read messages the bot can see, including in private channels it has access to.
- Get your bot flagged or disabled by Discord for abuse you didn’t commit.
The damage is limited only by the permissions your bot has. That’s the first lesson: a bot invited with Administrator turns a leaked token into a catastrophe.
How tokens actually leak
Almost every leak we see falls into one of these categories:
- Committed to a public repository. The token is pasted into
config.jsonor the main file, and the project is pushed to GitHub. Automated scrapers watch public commits and find tokens within minutes. - Left in git history. The token is removed from the code in a later commit — but it’s still in the history, where anyone can find it.
- Shared in a screenshot or paste. A helpful stranger asks for your code to debug it, or a screenshot of your editor shows the token.
- Printed to logs. Debug code logs the whole config object, and logs end up in a support ticket or a public channel.
- An
evalcommand. An owner-only eval command with a weak permission check lets someone runclient.token.
Discord participates in GitHub’s secret scanning programme, so a token pushed to a public repository is often detected and reset automatically. That protects your servers — but it also takes your bot offline until you notice, which is another reason to never commit it.
The fix: environment variables
The standard solution is to keep secrets out of code entirely and pass them to the process through environment variables. Your code reads a variable by name; the value lives somewhere else.
In Node.js:
client.login(process.env.DISCORD_TOKEN);
In Python:
import os
bot.run(os.environ["DISCORD_TOKEN"])
Where the value comes from depends on where the bot runs.
Locally: a .env file
For development, keep variables in a .env file and load it with dotenv (Node.js) or python-dotenv (Python):
DISCORD_TOKEN=paste-your-token-here
DATABASE_URL=mysql://user:pass@localhost:3306/bot
Then — before your first commit — add it to .gitignore:
.env
Commit a .env.example with the variable names and empty values, so collaborators know what to set without seeing your secrets. Using .env files in Node.js and Python walks through the setup for both languages.
In production: the host’s environment
On the server, you don’t upload the .env file at all. Instead, set variables in your host’s panel. On Kerit Cloud, environment variables are stored encrypted, injected into your bot’s container at start, and never printed in logs. Each bot runs in its own isolated container, so other customers can’t read your environment or files.
The same code works in both places: locally the value comes from .env, in production from the panel.
If a token has already leaked
Act immediately, in this order:
- Reset the token in the Discord Developer Portal under Bot → Reset Token. The old token stops working instantly.
- Update the environment variable on your host with the new token and restart the bot.
- Audit your servers. Check the audit log of servers your bot is in for actions you didn’t take.
- Clean the repository. Removing the token in a new commit is not enough. Either rewrite history with a tool like
git filter-repo, or — simpler — treat the old token as permanently public and rely on the reset. Never reuse it.
Resetting is the only step that actually protects you. Everything else is cleanup.
Reduce the blast radius
Even with good habits, assume a leak could happen and limit what it would cost.
Use least-privilege permissions. Invite the bot with only the permissions it needs. A music bot doesn’t need Manage Roles; a moderation bot doesn’t need Administrator. Server admins also appreciate bots that don’t ask for everything.
Lock down owner commands. If you have an eval or shell command, restrict it to your user ID — checked in code, not by role name — or remove it from production builds entirely.
const OWNER_ID = process.env.OWNER_ID;
if (interaction.user.id !== OWNER_ID) {
return interaction.reply({ content: 'Not allowed.', ephemeral: true });
}
Don’t log configuration objects. Log the fact that config loaded, not its contents. If you must debug, print variable names and whether they’re set.
Protect your Discord account. Enable two-factor authentication on the account that owns the application. For bots with several maintainers, create a Developer Portal team so ownership doesn’t hinge on one person’s account.
Rotate after staff changes. If someone with access to the token leaves the project, reset it.
Other secrets deserve the same care
Everything in this article applies to database passwords, API keys for AI services, payment provider keys and webhook URLs. Discord webhook URLs in particular are often overlooked — anyone with one can post to that channel. Treat every credential as an environment variable, and see keeping database credentials secure for database-specific advice.
A quick checklist
- [ ] Token read from
process.env/os.environ, never hard-coded - [ ]
.envin.gitignorebefore the first commit - [ ]
.env.examplecommitted with blank values - [ ] Production variables set in the host panel, not uploaded as a file
- [ ] Bot invited with minimum permissions
- [ ] Owner commands checked by user ID
- [ ] 2FA on the owning Discord account
- [ ] Token reset immediately after any suspected leak
Summary
Treat your bot token like a password with no expiry and no 2FA. Keep it in environment variables — a git-ignored .env file locally and encrypted panel variables in production — never in code, logs or screenshots. If it leaks, reset it first and clean up second, and limit the damage in advance with least-privilege permissions and locked-down owner commands.