Let's optimise your server!
Modern versions of Minecraft have a lot more things to process, and, as a result, need a bit more TLC in order to run as smooth as possible.
This guide aims to provide all the common steps taken by various Minecraft server networks, that have been tried and tested over a decade! Servers following this guide have reported that they were able to hold around 120 players at 19-20 TPS.
Important Information
There will never be a perfect, one size fits all way of fully optimising a Minecraft server, as every server is different! You should use this guide as a starter, and modify the values over time, as you gather more data on your server!
Another thing to note is that this guide cannot fix poorly developed plugins, a server with too much or too little RAM allocation, or a server run on poor hardware.
One of the first ways you can push for a better performance on your Minecraft server is by choosing a more optimised JAR file. However, whilst many pieces of software aim to stay as close to Vanilla Minecraft as possible, they have had to all make some sacrifices in order to achieve a smoother overall gameplay. Another downside is, the further up the forking ladder you go, the smaller the overall support and development communities will be.
Generally, it's also better to stay away from paid forks, as they often don't live up to all their promises, and just exist to try make a quick buck off inexperienced server owners. A lot of forks are often considered experimental, and should only be used if necessary. Paper is genuinely more than enough for most Minecraft servers; they have a growing community, and is maintained by well-known and experienced developers. However, if you do need a bit more performance, Pufferfish and Purpur are also ones to look at!
Once you have selected the JAR you plan to use for your Minecraft server, the next way to push better performance is by modifying your startup flags from the default set. For the most part, the standard JVM implementation will suffice. However, using an alternative implementation (OpenJ9+) may offer a slightly better performance, but, they are generally not directly supported by the maintainers of various different server softwares.
Depending on your Minecraft server implementation of choice, you will have various different gameplay configuration options that you can change to greatly improve your overall server performance! However, you shouldn't blindly copy and paste any values, but, rather use them as a base, and constantly make changes as you gather more information/data about your Minecraft server. Remember, in order to provide a smoother gameplay experiences, several options have to be modified, which may be different from (and may possibly break) Vanilla Minecraft.
Adding plugins is one of the best ways to customise your server, and standout from the rest! There are hundreds of thousands of plugins out there, and some are poorly coded. Poorly developed, poorly optimised or outdated plugins can use a lot of your servers resources, and begin to cause lag.
A popular misconseption about plugins is that premium plugins are better than free ones, which, more often than not, isn't the case. You should be looking for plugins that help you achieve your end goal, and not just use a premium plugin just because it's premium. One of the best ways of determining the quality of Minecraft plugins is how well known the developer is, and/or whether it's a well-established project.
One of the best ways to gain insight into what could be hindering performance on your Minecraft server is through Timings. Spigot offers Timings v1, which isn't as effective as v2, which is what Paper (and all forks of) provide you with.
Due to rewrites of the Chunk system over time, pregenerating your world isn't as important now, as it was back then. However, it's still beneficial. You should combine chunk/world pregeneration with a World Border (both by a plugin, and through Vanilla commands) to ensure that your players do not load any new chunks.
It's important to note that pregeneration takes a lot of time (often, hours), and uses a lot of server resources, and, as such, should be done before you release your server to your community. Something that is often overlooked is the fact that the Nether is 8x smaller than the Overworld, and players end up outside of the border, due to the size being too small.
The reason you want to use both a plugin and Vanilla for setting up a World Border is because you want to make sure that both plugin related data, and Vanilla data (treasure maps, eyes of ender, etc) only work from within your border.
You should be restarting your Minecraft server every 24 hours, and during your off-peak time (when you have the least amount of players onling). This allows the Garbage Collector, and RAM used by your Minecraft server to clear out useless, unneeded, and old data (players disconnecting, chunks information, entities, etc). When setting up auto restarts, you should be using a script to acomplish this, rather than a plugin.