RE:NODE

Minecraft join and quit messages: hide or change them

By RE:NODE Team
14 min read


Vanilla Minecraft has no setting that hides or changes the yellow "joined the game" and "left the game" lines. There is no gamerule, no server.properties key and no datapack hook for them. To change them you need a plugin, and on a Paper server the usual answer is EssentialsX: custom-join-message and custom-quit-message in its config.yml, where "none" keeps the vanilla message and "" hides it completely. Staff who should arrive unannounced get allow-silent-join-quit: true plus the essentials.silentjoin and essentials.silentquit permissions. The one request no config file covers is "show the message to everyone except the player who joined" - that needs a few lines of Skript or a tiny plugin, both shown below.

Why vanilla has no switch for it#

The join message is not text the server writes. When a player logs in, the server sends every connected client a translatable component - the key multiplayer.player.joined with the player's name as its argument - and each client renders it in its own language. Leaving uses multiplayer.player.left, and a player whose name has changed since their last visit gets multiplayer.player.joined.renamed, the "(formerly known as ...)" line. The broadcast goes to everyone online, including the player who just arrived, and it is also printed to the server console.

Mojang added gamerules for many broadcast messages over the years - death messages, advancement announcements, command feedback - but never one for joins and quits. That is the whole reason this question keeps coming up: people search the gamerule list, find showDeathMessages and announceAdvancements, and assume a join equivalent must exist. It does not. Recent versions have also been renaming gamerules (1.21.11 moved them to lowercase-with-underscores names such as show_death_messages), so if you are hunting for the death or advancement switch, tab-complete /gamerule on your own version rather than trusting an old list.

What vanilla does have, for completeness:

  • A server resource pack can override the translation. Setting multiplayer.player.joined to an empty string in a pack's language file changes what clients display, but it only works for players who accept the pack, it leaves an empty chat line on some versions, and it changes the text in singleplayer too while the pack is loaded. It is a curiosity, not a fix. MOTD, icon and resource packs covers how a server pack is delivered.
  • Nothing on the console side. The console log line cannot be suppressed without a plugin either.

On a vanilla or Fabric server, then, the honest answer is "switch to Paper, or find a mod that patches the message". The rest of this guide assumes Paper or a Paper fork, which is what most plugin servers run.

What Paper and Spigot can and cannot change#

Neither Paper nor Spigot exposes the join message in its own config files. spigot.yml has a messages: block, but it covers the whitelist kick, unknown command, server full, outdated client and server, and restart texts - not joins. Paper's config/paper-global.yml has a messages: section with one related key:

config/paper-global.yml
messages:  use-display-name-in-quit-message: false

With true, the quit message uses the player's display name (which a nickname or prefix plugin may have changed) instead of their username. It does not hide the message, and there is no matching key for joins.

What Paper does give you is the API. Every join fires a PlayerJoinEvent whose message any plugin can replace or clear, and every quit fires a PlayerQuitEvent likewise. Paper uses Adventure components for these, so a plugin can set a fully formatted message with hex colours, hover text and click actions. That is how EssentialsX, vanish plugins and chat plugins all do it - and it is also why two of them installed together can fight over the same message, covered in troubleshooting below.

WhereJoin/quit controlNotes
Vanilla, FabricNoneOnly mods or a resource pack hack
spigot.ymlNoneOther kick and error texts only
paper-global.ymlQuit name onlyuse-display-name-in-quit-message
PluginsFullThrough the join and quit events

EssentialsX custom-join-message and custom-quit-message#

If EssentialsX is already on the server, you do not need anything else for the common cases. The keys sit in plugins/Essentials/config.yml in the core jar, so no module is required. The defaults:

plugins/Essentials/config.yml
custom-join-message: "none"custom-quit-message: "none"custom-new-username-message: "none"hide-join-quit-messages-above: -1

The two values that matter are easy to mix up, and plenty of forum answers get them backwards:

  • "none" means Essentials does not touch the message - players see the normal vanilla "joined the game" line.
  • "" (an empty string) means hide the message entirely.

So hiding both messages for everyone is:

yaml
custom-join-message: ""custom-quit-message: ""

Then run /essentials reload (from the console, essentials reload with no slash) and test by logging in on a second account. The general mechanics of the file - YAML rules, what needs a restart, which sections depend on module jars - are in the EssentialsX config.yml guide, so they are not repeated here.

To replace the message instead of hiding it, write your own text with & colour codes and the placeholders Essentials provides:

PlaceholderBecomes
{PLAYER}The display name (nickname, prefix)
{USERNAME}The account name
{PREFIX} / {SUFFIX}Prefix and suffix from the permissions plugin
{ONLINE}Players online now
{UNIQUE}Unique players who have ever joined
{UPTIME}How long the server has been running
yaml
custom-join-message: "&8[&a+&8] &7{USERNAME}"custom-quit-message: "&8[&c-&8] &7{USERNAME}"

That compact [+] name / [-] name style is popular on busy servers because it carries the information in a quarter of the space. A friendlier small-server version might be "&e{PLAYER} &7has arrived - {ONLINE} online".

A few details worth knowing before you settle on a format:

  • `{PLAYER}` versus `{USERNAME}`. {PLAYER} includes whatever the display name has picked up - a /nick, a rank prefix from LuckPerms through Vault. If your chat format already shows ranks, a join line with {PLAYER} can be long and noisy. {USERNAME} is always the plain account name.
  • Prefixes need Vault. {PREFIX} and {SUFFIX} come from your permissions plugin through Vault. With LuckPerms but no Vault they come out empty. The LuckPerms guide covers setting prefixes per group.
  • Quote the value. A message starting with & or containing a colon has to be in quotes or the YAML will not parse, and Essentials quietly keeps the old value.

Renamed players

custom-new-username-message is used instead of custom-join-message when a returning player has changed their Mojang username since their last visit. With its default of "none", Essentials uses your normal custom join message for them too. If you want to point out renames, it also accepts {OLDUSERNAME}. If you set a custom join message, check this key as well, otherwise renamed players get a message in a different style from everybody else.

Hiding messages only when the server is busy

hide-join-quit-messages-above is the setting large servers want and small ones do not. With -1 (the default) messages are always shown. Set it to a number and join and quit messages stop once that many players are online:

yaml
hide-join-quit-messages-above: 40

On a server with sixty people and constant churn, join spam pushes real conversation off the screen; on a server with eight friends, the join line is how you know someone is on. This key lets the same config serve both phases of a server's life.

Hiding a player's own join message from them#

The topic that brings many people here is narrower: everyone else should see "Alex joined the game", but Alex should not, because they already know, and on a server with a welcome message or MOTD in chat it is one more line pushing the useful text away.

No EssentialsX key does this. The join event has one message that is broadcast to everyone; it has no per-recipient list. The way round it is to clear the event's message and send your own copy to every player except the one who joined. In Skript, if you already run it:

plugins/Skript/scripts/joinmessage.sk
on join:    set join message to ""    loop all players:        loop-player is not player        send "&e%player% joined the game" to loop-player

The loop-player is not player line is the condition that skips the joining player. Skript is a heavy plugin to install for this alone, though. If you or someone on your team writes Java, the same thing is a dozen lines in a Paper plugin's listener:

JoinListener.java
@EventHandlerpublic void onJoin(PlayerJoinEvent event) {    Component message = event.joinMessage();    event.joinMessage(null);    if (message == null) return;    Player joined = event.getPlayer();    for (Player p : Bukkit.getOnlinePlayers()) {        if (!p.equals(joined)) p.sendMessage(message);    }    Bukkit.getConsoleSender().sendMessage(message);}

Two things in that snippet are deliberate. Reading joinMessage() first and re-sending it means whatever EssentialsX or another plugin already set is preserved - your listener only changes who receives it. And the console line puts the join back in the server log, because clearing the event's message also removes it from the console, which makes later moderation harder. Register the listener at a late priority (@EventHandler(priority = EventPriority.HIGHEST)) so it runs after the plugins that format the message.

Do not combine this with custom-join-message: "" in Essentials. If Essentials clears the message first, your listener finds nothing to forward and nobody sees a join at all.

Silent join for staff#

Staff often want to log in without announcing themselves - to watch a suspected cheater, to check something at 3 a.m., or just because a moderator joining makes the chat behave differently. EssentialsX handles this with one config key and three permissions:

plugins/Essentials/config.yml
allow-silent-join-quit: true
PermissionEffect
essentials.silentjoinNo join message for this player
essentials.silentquitNo quit message for this player
essentials.silentjoin.vanishAlso vanished on arrival

None of the permissions do anything while allow-silent-join-quit is false, which is the default, so turning the key on is the step people miss. Grant the nodes to a group rather than to individuals:

bash
$ lp group moderator permission set essentials.silentjoin true$ lp group moderator permission set essentials.silentquit true$ lp group moderator permission set essentials.silentjoin.vanish true

Think about whether you want the vanish node as well. A silent join with no vanish still puts the player in the tab list, the /list output and the sleeping-player count, so anyone paying attention sees them arrive within seconds. Silent join only removes the chat line. Watch for one trap on wildcard groups: a group with essentials.* holds all three nodes, so admins who expected to be announced normally will not be. Set the nodes to false explicitly on that group if you want the wildcard but not the silence.

Vanish plugins and fake join or quit messages#

Vanish is the other half of the staff problem. EssentialsX has /vanish (permission essentials.vanish), which hides a player from others' tab list and view. Its related keys:

  • hide-displayname-in-vanish: true - keeps a vanished player's display name out of places that would reveal them.
  • sleep-ignores-vanished-player: true - a vanished admin does not block the night skip.
  • last-message-reply-vanished: false - players cannot /reply to a vanished player they cannot see, which would otherwise give the game away.

Essentials' /vanish does not post a fake "left the game" line when someone vanishes, so a moderator who vanishes mid-conversation simply disappears from the tab list with no explanation. That is where the dedicated vanish plugins earn their place. SuperVanish (free) and PremiumVanish (paid, same author) can broadcast a fake quit message when a player vanishes and a fake join message when they reappear, hide real join and quit messages for players who log in vanished, and integrate with other plugins so vanished staff do not appear in places like web maps or Discord bridges. Their options live in their own config files; read the generated config.yml after the first start rather than copying keys from an old forum post, because the layout has changed between versions.

Use one vanish system, not two. Running SuperVanish beside Essentials' /vanish gives you two different ideas of who is hidden, and whichever one the other plugins ask gets believed.

First-join messages and welcomes#

The first time a player ever joins, two messages can appear: the normal join message (vanilla or your custom one) and Essentials' new-player broadcast from the Spawn module.

yaml
newbies:  announce-format: '&dWelcome {DISPLAYNAME}&d to the server!'  spawnpoint: newbies  kit: tools

announce-format is broadcast to everyone, only on a player's first ever join, and only if the EssentialsX Spawn jar is installed - without it, the newbies: section is not read at all. Set it to '' to turn the welcome off. Note the placeholder here is {DISPLAYNAME}, not {PLAYER}; the two sections use different names for the same thing, and copying one into the other prints the literal text. The kit and spawnpoint lines in the same block are covered in EssentialsX kits, warps and homes.

A good pattern on a community server is a quiet normal join ([+] name) plus a louder first-join welcome, so regulars notice and greet the new player without the chat being full of yellow lines all evening. If you want a private greeting that only the new player sees - rules, a link to the Discord - that is a message of the day rather than a join message. EssentialsX shows the motd.txt file in its folder to each player on login, and only to them.

Colours, hex and MiniMessage#

custom-join-message and custom-quit-message take the legacy & codes: &a to &f and &0 to &9 for colours, &l bold, &o italic, &r reset. That covers most needs.

MiniMessage is the tag-based format from the Adventure library that Paper uses - <green>, <gradient:#ff0000:#0000ff>, <hover:show_text:'...'>. It is what modern plugins increasingly expect, and it is the only practical way to put hover text or click actions on a join line. EssentialsX moved its own language files to MiniMessage-style tags in the 2.21 releases (which is where the message-colors primary and secondary colours in config.yml come from), but the join and quit keys in config.yml still document & codes and placeholders. If you want a gradient or a clickable join message, set the message from a plugin that supports MiniMessage, or from your own listener with MiniMessage.miniMessage().deserialize(...), and leave Essentials' keys on "none" so the two do not collide.

Whatever you use, keep join lines short. They appear dozens of times a day, and a two-line gradient banner stops being charming by the second evening.

Networks, Discord and other places joins appear#

Hiding the in-game line does not hide a join everywhere.

  • Proxy networks. Behind Velocity or BungeeCord, each backend server fires its own join and quit events. A player moving from the lobby to survival "leaves" the lobby and "joins" survival, so both servers announce it, and players see join spam every time someone changes server. The usual fix is to hide join and quit messages on every backend (custom-join-message: "" on each) and use a proxy-side plugin for network-wide join and leave messages. Velocity proxy networks covers the layout.
  • Discord bridges. DiscordSRV posts its own join and leave messages to the linked channel, configured in its own files. Hiding them in game does nothing there - see DiscordSRV and Discord integration and check its vanish integration if staff join silently.
  • The tab list and `/list`. A hidden join message still leaves the player visible in both, unless they are vanished.
  • The console. Clearing the message in the event also removes it from the log. If moderation depends on the log, re-send it to the console as in the listener above.

Troubleshooting#

`custom-join-message: "none"` still shows the message. That is correct behaviour - "none" means vanilla. Use "" to hide it.

Changes do nothing after reload. The YAML did not parse (an unquoted & at the start of the value is the usual cause), or you edited a copy of the file. Watch the console when you run /essentials reload; a parse error names the line.

Two join messages appear. Two plugins are both setting one - Essentials and a chat or join-message plugin, or Essentials and SuperVanish. Leave one of them on its default. If one of the two is the first-join announce-format, that is expected on a first join only.

Staff with silent join are still announced. allow-silent-join-quit is still false, or the permission is set on a group the player does not inherit. lp user <name> permission check essentials.silentjoin shows what Essentials sees.

Admins are joining silently when they should not. A wildcard such as essentials.* includes the silent nodes. Set them to false on that group.

Messages vanished entirely after installing a plugin. Something cleared the event message at a higher priority. Remove plugins one at a time, or run /plugins and look for anything with "join" or "chat" in its name. Plugins worth installing covers why two plugins doing one job causes exactly this.

FAQ#

Is there a gamerule to turn off join messages?

No. There are gamerules for death messages and advancement announcements, but join and leave messages have never had one in Java Edition. You need a plugin or a mod.

Does "none" hide the EssentialsX join message?

No, it does the opposite: "none" tells Essentials to leave the vanilla message alone. An empty string, "", hides it.

Can I hide the join message only for the player who joined?

Not with a config setting. The join message is one broadcast to everyone. Clear it and re-send it to every player except the joiner, with Skript or a small listener.

Can players with a rank join without being announced?

Yes. Set allow-silent-join-quit: true in Essentials and give their group essentials.silentjoin and essentials.silentquit. Add essentials.silentjoin.vanish if they should also be invisible on arrival.

Will hiding join messages stop Discord showing joins?

No. Discord bridges such as DiscordSRV send their own join and leave notices, configured separately in the bridge's files.