Guide
How to make a Folia plugin
Updated October 18, 2026
Folia is a fork of Paper that splits the world into regions and ticks them on different threads, so large servers can use many CPU cores. There is no single main thread anymore, and that breaks the usual Bukkit scheduler. A Folia plugin is a Paper plugin written for that model, and it must say so in its plugin.yml.
An example with the timing details Folia needs.
Generate this with CodexeStep by step
1.Check that you need Folia
Folia helps servers with many players spread out across the world. Most servers run Paper; many popular plugins do not support Folia, so check the ones you rely on first.
2.Build against the Folia API
Use the
folia-apidependency for your version from the PaperMC repository, withprovidedscope, in a Maven or Gradle project.3.Replace the Bukkit scheduler
Bukkit.getScheduler()does not work on Folia. Use the region scheduler for work at a location, the entity scheduler for work on a player or mob, the global region scheduler for world-wide tasks, and the async scheduler for I/O.4.Touch things only from their own region
Change a block or an entity only on the thread that owns it. Use
teleportAsyncinstead ofteleport, and schedule onto the target's region before changing it.5.Declare Folia support
Add
folia-supported: truetoplugin.yml. Without it Folia refuses to load the plugin.6.Test on a Folia server
Compiling proves the code builds against the Folia API, not that it is thread safe. Run it on a Folia test server with several players in different places.
The four schedulers
| Scheduler | Get it with | Use it for |
|---|---|---|
| Region | Bukkit.getRegionScheduler() | Work at a world location: placing blocks, spawning |
| Entity | entity.getScheduler() | Work on one player or mob, it follows them across regions |
| Global region | Bukkit.getGlobalRegionScheduler() | World time, weather, things not tied to a location |
| Async | Bukkit.getAsyncScheduler() | Database, files, web requests, never the world |
What usually breaks
- Repeating tasks for all players: a single loop over every online player no longer works; schedule a task on each player's entity scheduler instead.
- Teleports and warmups: "teleport after 3 seconds" needs the entity scheduler and
teleportAsync. - Shared data: maps and counters used from several regions must be thread safe, for example concurrent collections.
- Dependencies: a library that calls the Bukkit scheduler throws
UnsupportedOperationExceptionon Folia.
One plugin for Paper and Folia
The region, entity, global and async schedulers are also part of the Paper API on current versions, so a plugin that uses only them and declares folia-supported: true can run on both Paper and Folia. That is often the best target: one .jar for every server.
With Codexe
Choose the Folia type. The generated plugin schedules work with the region, entity, global region and async schedulers, declares folia-supported: true and builds against the Folia API with Maven. It is compiled and checked before it is Ready.
Put timing details in the request ("every minute for each online player", "teleport after 3 seconds"): that is where Folia code differs most. Try it now does not run Folia servers yet, so test the .jar on your own Folia server.
Questions
Do Paper plugins run on Folia?
Only if they are written for it and declare folia-supported: true. A normal Paper plugin that uses the Bukkit scheduler is refused or fails.
Does a Folia plugin run on Paper?
Yes, when it uses only APIs that Paper also has, which includes the Folia schedulers on current Paper versions.
My Folia plugin throws an exception about the scheduler
Some code, often from a library, still uses the Bukkit scheduler. Ask the project chat to move it to the region or entity scheduler.
Make a Folia plugin
This prompt opens ready to send:
“A Folia 1.21.4 plugin called RTP: /rtp teleports the player to a random safe location within 5000 blocks after a 3-second warmup that cancels on movement, using the entity scheduler and teleportAsync, a 10-minute cooldown per player saved in SQLite, folia-supported: true, radius, cooldown and messages in config.yml.”
Generate this with Codexe