Java's most popular Discord library is JDA (the Java Discord API), and hosting a JDA bot on Falix is just Java hosting: build a runnable fat jar, upload it, start. This guide walks the whole path — a Maven project, the shade plugin that bundles JDA into your jar, reading the token from a file, and a /ping slash command — then runs it to the point where a real token would connect.
| At a glance | |
|---|---|
| You need | a bot token from Your first Discord bot, Maven on your own computer, and a Falix server running the Java application |
| Plan | Free or premium — free runs while your session timer has time, premium runs 24/7 |
| Time | about forty minutes |
Two guides own the parts around this one: Java on Falix explains the one-jar model and the build mechanics in full, and JDA vs the alternatives covers why JDA over Javacord or Discord4J. This guide is the hands-on build.
The model in one line
The Java application runs one runnable jar — java -jar app.jar, essentially. Nothing compiles on the server, so every library your bot uses must be packed inside that jar (a "fat jar"). And a pure bot makes only outbound connections to Discord, so it needs no port — SERVER_PORT is irrelevant. Build the fat jar locally, upload it, start.
Step 1 — The Maven project
Create a project folder with this pom.xml. It pulls in JDA and configures the Shade plugin to build a fat jar named app.jar with your class as the entry point:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>discord-bot</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>5.2.1</version>
</dependency>
</dependencies>
<build>
<finalName>app</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<phase>package</phase>
<goals><goal>shade</goal></goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.bot.Bot</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Two lines carry the weight: <finalName>app</finalName> makes the output app.jar (matching the JAR FILE variable's default), and the ManifestResourceTransformer's mainClass is what makes the jar runnable — without it you get the classic no main manifest attribute error.
🎯 Good to know: JDA moves quickly — check jda.wiki for the current version and drop it into
<version>. Compiling withrelease17 keeps the bytecode comfortably runnable on Falix's Java 21 default; don't compile with a newer JDK than the runtime you'll pick, or you'll hitUnsupportedClassVersionError.
Step 2 — The bot code
Create src/main/java/com/example/bot/Bot.java. It reads the token from a file, connects, registers a /ping slash command when ready, and answers it:
package com.example.bot;
import java.nio.file.Files;
import java.nio.file.Path;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.events.session.ReadyEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
public class Bot extends ListenerAdapter {
public static void main(String[] args) throws Exception {
// Read the token from token.txt, sitting next to app.jar (never hard-code it).
String token = Files.readString(Path.of("token.txt")).trim();
JDABuilder.createDefault(token)
.addEventListeners(new Bot())
.build();
}
@Override
public void onReady(ReadyEvent event) {
// Register slash commands once the bot is ready:
event.getJDA().updateCommands()
.addCommands(Commands.slash("ping", "Check that the bot is alive"))
.queue();
System.out.println("Listening as " + event.getJDA().getSelfUser().getName());
}
@Override
public void onSlashCommandInteraction(SlashCommandInteractionEvent event) {
if (event.getName().equals("ping")) {
long ms = event.getJDA().getGatewayPing();
event.reply("Pong! " + ms + " ms").queue();
}
}
}
The shape mirrors every JDA bot: JDABuilder builds the connection, a ListenerAdapter subclass overrides the events you care about, onReady registers the slash commands with Discord (updateCommands().addCommands(...)), and onSlashCommandInteraction answers them. .queue() sends the request asynchronously — JDA's normal way to call Discord without blocking.
💡 Tip:
updateCommands()registers global commands, which can take a few minutes to appear the first time — the same first-time delay as the other libraries. To test instantly, register on a single guild withevent.getGuild().updateCommands()during development.
Step 3 — Build the fat jar
From your project folder:
mvn package
Maven compiles your code and the Shade plugin bundles JDA and its dependencies into target/app.jar — a single self-contained file (JDA alone makes this around 19 MB, which is expected). Test it locally if you like: put a token.txt next to it and java -jar app.jar. If it starts on your machine, it starts on Falix.
Step 4 — Get the jar and token onto the server
Two files go up to /home/container:
app.jar— drag it into the File Manager, or copy it over SFTP. (See Java on Falix for the upload and Git build-on-deploy paths.)token.txt— create it in the File Manager and paste your bot token as the only line. The code reads this at startup, so your token never lives inside the jar and you can rotate it without rebuilding.
⚠️ Heads up: Keep
token.txtoff Git and treat it like a password — anyone with it controls your bot. The Java application has no.envauto-loading like Node or Python, which is exactly why a small token file read at startup is the clean pattern here.
Step 5 — Start it
Press Start on the Console page. With a valid token, JDA connects and your onReady prints:
Listening as YourBotName
That's the success signal. In Discord, /ping replies with the gateway latency after the first-time global-command delay.
Verify it works
Type /ping in a server the bot is in. A Pong! … ms reply means the whole chain worked: the jar ran, JDA connected with your token, the command registered, and the handler answered. No reply after a few minutes? Re-invite the bot with the applications.commands scope (see Your first Discord bot).
Troubleshooting
InvalidTokenException: The provided token is invalid!— the token intoken.txtis wrong, was reset, or the file has extra whitespace..trim()handles stray newlines; copy a fresh token from the Developer Portal (resetting invalidates the old one). See Bot appears offline.no main manifest attribute, in app.jar— the jar isn't runnable. Yourpom.xmlis missing theManifestResourceTransformerwithmainClass, or you uploaded the wrong jar (Shade also leaves anoriginal-app.jar— don't upload that one).NoClassDefFoundErrorthe moment it touches JDA — the jar isn't a fat jar; the Shade plugin didn't bundle the dependencies. Confirmmvn packageran theshadegoal and thatapp.jaris the large (~19 MB) one.UnsupportedClassVersionError— you compiled with a newer JDK than the runtime. Setmaven.compiler.releaseno higher than the Java version picked in Settings (default 21).NoSuchFileException: token.txt— the file isn't in/home/containernext to the jar. Create it in the File Manager.
Next steps
- Java on Falix — the one-jar model and Git build-on-deploy in full
- JDA vs Javacord vs Discord4J
- Deploy your code with Git — build the jar on deploy instead of uploading
- JDA's own documentation is at jda.wiki