Hosting a JDA Java Discord Bot
Package a JDA 5 Discord bot as a single runnable JAR, size the JVM heap for your plan, and deploy it to an always-on Java 17 or 21 server.
On this page
Java bots built with JDA are fast, strongly typed and scale well — but they deploy a little differently from Node.js and Python bots. You compile ahead of time, package everything into a JAR, and tell the JVM how much memory it may use. Get those three things right and hosting a JDA bot is straightforward.
Pick a Java version
JDA 5 runs on Java 8 and newer, but there’s no reason to start a new bot on an old runtime. Use Java 17 or 21 — both are long-term-support releases with better garbage collectors and faster startup. Kerit Cloud offers Java 11, 17 and 21; compile for the same version you’ll run on.
A minimal JDA 5 bot
package bot;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
public class Main extends ListenerAdapter {
public static void main(String[] args) throws InterruptedException {
String token = System.getenv("DISCORD_TOKEN");
if (token == null || token.isBlank()) {
throw new IllegalStateException("DISCORD_TOKEN is not set");
}
JDA jda = JDABuilder.createLight(token)
.addEventListeners(new Main())
.build()
.awaitReady();
System.out.println("Ready as " + jda.getSelfUser().getAsTag());
}
@Override
public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
if (event.getName().equals("ping")) {
event.reply("Pong! " + event.getJDA().getGatewayPing() + " ms").queue();
}
}
}
Two choices here keep the bot lean. JDABuilder.createLight disables most caches by default, which matters a lot for memory; use createDefault only if you need cached members and presences. And the token comes from an environment variable, never from source — see keeping your bot token safe.
Registering the command
Slash commands are registered separately from startup logic. A simple approach is a one-off registration you trigger when commands change:
jda.updateCommands()
.addCommands(Commands.slash("ping", "Show the gateway latency"))
.queue();
updateCommands() replaces the full global command list, so include every command in the call. Don’t run it on every start: each deploy restarts the bot, and repeated registration wastes API calls. Guard it behind an environment variable such as REGISTER_COMMANDS=true that you set only when needed.
Build a single runnable JAR
The server needs one file that contains your code and all dependencies — a “fat” or “shadow” JAR. With Gradle (Kotlin DSL):
// build.gradle.kts
plugins {
java
application
id("com.gradleup.shadow") version "8.3.5"
}
repositories { mavenCentral() }
dependencies {
implementation("net.dv8tion:JDA:5.2.1") // use the latest 5.x release
implementation("ch.qos.logback:logback-classic:1.5.12")
}
java {
toolchain { languageVersion.set(JavaLanguageVersion.of(21)) }
}
application { mainClass.set("bot.Main") }
Build it with:
./gradlew shadowJar
# → build/libs/<project>-all.jar
The Logback dependency isn’t optional in practice. JDA logs through SLF4J, and without a logging implementation you’ll see a “No SLF4J providers were found” warning and lose all of JDA’s useful connection logs.
If you prefer Maven, the maven-shade-plugin does the same job and produces a JAR with dependencies included. Kerit Cloud’s git deploys support Maven builds as well as uploading a prebuilt JAR.
Size the JVM heap
This is where most Java bot hosting problems come from. The JVM needs to know how much memory it may use, and the heap is not the whole process — the JVM also needs memory for metaspace, thread stacks, the JIT compiler and native buffers. If you set the heap equal to your plan’s RAM, the process exceeds the limit and gets killed.
A safe rule is to give the heap about 70–75% of your plan’s memory:
java -XX:MaxRAMPercentage=75 -jar bot-all.jar
MaxRAMPercentage reads the container’s memory limit and sizes the heap from it, so the same command works when you change plans. Explicit sizes work too:
| Plan RAM | Suggested heap |
|---|---|
| 512 MB | -Xmx360M |
| 1 GB | -Xmx750M |
| 3 GB | -Xmx2200M |
| 6 GB | -Xmx4500M |
A small JDA bot with createLight typically runs well in 512 MB. Bots that cache members for large servers need more — see how much RAM a Discord bot needs.
For garbage collection, the default G1 collector is a good choice for bots. On Java 21 with larger heaps, generational ZGC (-XX:+UseZGC -XX:+ZGenerational) keeps pauses very short, though it uses a bit more memory.
Deploy it
- Create a server on a Discord bot plan and choose a Java 17 or 21 runtime matching your toolchain.
- Either link your repository for git deploys with a Gradle or Maven build, or upload the built
-all.jarthrough the file manager or SFTP. - Add
DISCORD_TOKENas an environment variable in the panel. - Set the startup command, for example
java -XX:MaxRAMPercentage=75 -jar bot-all.jar. - Start the server and watch the console for your “Ready as…” line.
If the bot crashes, the watchdog restarts it within seconds and the stack trace stays in the console log.
Handling errors and shutdowns
JDA’s queue() runs requests asynchronously; pass a failure callback so errors are logged rather than lost:
event.reply("Done").queue(
success -> {},
error -> logger.warn("Reply failed in guild {}", event.getGuild().getId(), error)
);
For clean shutdowns, register a hook that closes JDA and any database pool when the host stops the process:
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
jda.shutdown();
dataSource.close();
}));
The JVM runs shutdown hooks on SIGTERM, which is what hosts send during restarts and redeploys.
Common problems
OutOfMemoryError: Java heap space — the heap is too small for your caches. Trim caching with createLight and cache flags, or raise the heap within your plan’s limits.
The process disappears with no error — the whole JVM exceeded the container limit, usually because the heap was set too close to the plan’s RAM. Lower -Xmx or use MaxRAMPercentage=75.
UnsupportedClassVersionError — you compiled for a newer Java than the server runs. Match the toolchain version to the runtime.
Disallowed intents — a privileged intent is requested in code but not enabled in the Developer Portal.
Summary
Host a JDA bot by compiling for Java 17 or 21, packaging a single shadow JAR with Logback included, and starting it with a heap of about 75% of your plan’s memory. Use createLight to keep caches small, register commands only when they change, and add a shutdown hook so restarts are clean.