How to host a JDA (Java) Discord bot
A JDA bot that runs from your IDE usually fails on a server for one reason: the jar you built does not contain JDA. Fix that and hosting a Java bot is simpler than Python or JavaScript, because there is nothing to install.
7 minute read
1. Build one jar with everything in it
Your IDE puts JDA and every other library on the classpath for you. java -jar on a server does not, so a plain jar stops with NoClassDefFoundError: net/dv8tion/jda/api/JDABuilder. Build a "fat" jar that includes the dependencies.
With Gradle, the Shadow plugin:
plugins {
id 'java'
id 'application'
id 'com.gradleup.shadow' version '8.3.5'
}
repositories { mavenCentral() }
dependencies {
implementation 'net.dv8tion:JDA:5.2.1'
}
application {
mainClass = 'com.example.bot.Main'
}
./gradlew shadowJar writes build/libs/yourbot-all.jar. With Maven, the assembly plugin's jar-with-dependencies descriptor does the same job. Check the result on your own machine, outside the IDE: java -jar build/libs/yourbot-all.jar.
The JDA version above is only an example: use the current one from JDA's releases.
2. Read the token from the environment
public class Main {
public static void main(String[] args) {
String token = System.getenv("BOT_TOKEN");
if (token == null || token.isBlank()) {
System.err.println("BOT_TOKEN is not set");
System.exit(1);
}
JDA jda = JDABuilder.createLight(token, GatewayIntent.GUILD_MESSAGES)
.addEventListeners(new Commands())
.build();
Runtime.getRuntime().addShutdownHook(new Thread(jda::shutdown));
}
}
A token in a config.json inside the jar ends up in every copy of the jar you ever share. An environment variable does not.
3. Ask only for what you use
JDABuilder.createLightstarts with the member and presence caches off, which is what most bots want and keeps memory low.createDefaultcaches more.MESSAGE_CONTENT,GUILD_MEMBERSandGUILD_PRESENCESare privileged: switch them on in the Developer Portal too, or the bot fails at login with a disallowed intents error.- Slash commands need no privileged intent. Register them with
jda.updateCommands().addCommands(...).queue()once, not on every event.
4. Give the JVM a sensible heap
On its own the JVM sizes its heap at a quarter of the machine's (or container's) memory. On a 1 GB server that is 256 MB, and a busy bot runs out long before the server does. Start it with a percentage instead:
java -XX:MaxRAMPercentage=75.0 -jar yourbot-all.jar
That leaves a quarter for the JVM's own overhead and everything else.
5. Shut down cleanly
The shutdown hook in step 2 closes JDA's connection when the process is told to stop (Ctrl+C at home, a stop signal on a server), so Discord does not keep a ghost session open and the bot comes back online quickly on restart.
Keeping it running
On your own Linux machine, a systemd unit with ExecStart=/usr/bin/java -XX:MaxRAMPercentage=75.0 -jar /opt/mybot/yourbot-all.jar and Restart=on-failure does it; the systemd guide has the rest. The 24/7 guide compares the other options.
On SnowServers, pick Java at checkout (17, 21 or 25), upload the jar, paste the token into the panel, and the heap is set for you. Stop runs the shutdown hook from above, so keep it in your code. Flurry is $3 a month with a 7-day free trial. Java bots in our docs.