Flarum 2.0.0-rc.6 Released - a performance release, first and foremost.
This is a big one. 2.0.0-rc.6 is over 100 merged PRs (could this be the one?), and if you follow the threads back, most of them lead to the same place: making Flarum faster. Fewer queries per page, less data loaded that you never asked for, work done in parallel instead of one-thing-after-another, and a caching layer that stops tripping over itself under load. The rest is a round of small, modern touches to the interface โ and a pile of the reliability fixes that only surface once thousands of people run the software for real.
If you only read one line: this release makes existing forums quicker without you changing anything. Upgrade, and the same pages do less work to render.
๐ Upgrading is composer update -W. A couple of external dependencies moved this cycle (we switched the sourcemap package and raised the Symfony floor), so use the -W flag so Composer can update the dependencies-of-dependencies along with them โ a plain composer update may not resolve. Take a backup, run it on staging if you have one, and keep a way back โ same as always.
๐ You're already looking at it. discuss.flarum.org has been progressively running, testing, and refining this work since the last release โ the speed-ups below were shaken out here, on a real-world forum, before they were tagged. discuss is now on the final 2.0.0-rc.6, exactly the release you'll download. The nightly demo rebuilds from 2.x every day too.
๐ฌ Ran it? Say so. A one-line "upgraded, feels snappier" inSupport is worth more than you'd think to the next person hovering over the button.
โก The headline: performance
The theme of this cycle is speed, and it shows up across the whole request lifecycle โ the database, the API layer, and the boot. None of it changes how anything behaves. It just gets there with less work.
๐๏ธ Fewer, smarter queries
The biggest wins are in cutting query counts on the pages people hit most:
- The discussion and its post window now load in parallel instead of one after the other, so opening a discussion spends less time waiting on the database. (#4862)
- A single-discussion fast path short-circuits the posts ID-linkage query when you're only looking at one discussion. (#4863)
- Post visibility is materialized once and relation aggregates are grouped flat, instead of being recomputed piecemeal. (#4866)
- Limited relation includes load in two narrow phases rather than one broad, expensive pass. (#4867)
- Endpoint eager-loads point back at their parent models, so the API stops re-fetching what it already had. (#4877)
- The tags min-tags policy went from four
COUNT queries down to at most one. (#4865)
- Mentions eager-loads the discussion of mentioning posts, closing an N+1. (#4876)
- Sticky serves its excerpt as an attribute instead of including whole posts to get it. (#4882)
๐๏ธ Less work per request
- Post-stream chunks are prefetched after boot, so scrolling has the next pieces ready before you reach them. (#4838)
- Model casts are resolved once per class and cached, instead of being worked out again for every model instance. (#4875)
- Permissions are cached per group set rather than per
User, so a page full of users from the same groups computes permissions once, not once each. (#4839)
๐งช And a guard so it stays fast
Performance regressions are easy to reintroduce and hard to notice. So the test suite now fails an integration test if it runs an N+1 query โ the class of bug behind a surprising number of the fixes above. It caught its first offender on the way in. From here, that whole category of slowdown gets stopped at CI instead of in production. (#4871)
๐ฃ A note for extension authors: help us keep it fast
Here's the good part. We made core faster โ but core is only half the story. While doing this work, we looked across the ecosystem and found plenty of extensions that can quietly undo these gains: the same N+1 query patterns, default includes that drag in data nobody asked for, and eager-loads that fan out across a page. On a forum running a stack of them, a fast core can still feel slow โ and that's not something we can fix from here. It's the extension's to fix.
The good news is you now have the same tool we used. This release ships a new flarum/testing that carries the N+1 detection from #4871. Pull it into your extension's test suite and it will flag N+1 queries for you, the same way it now flags them for core โ turning "this feels slow on big forums" into a concrete, reproducible test failure you can actually chase down.
If you maintain an extension, this is the ask:
- Bump to the new
flarum/testing (^2.0.0-rc.6) and run your integration tests. Watch for N+1 failures.
- Audit your default includes โ does your extension pull relations into the discussion list or post stream that most pages don't need? The core changes above (#4796 from rc.5 onward, and the eager-load rework this cycle) are worth reading as worked examples of the pattern to follow.
- Lean on the extensibility Flarum already gives you โ buffered/lazy includes, per-endpoint eager-loading, and scoped relation loading are all there to keep your extension's cost proportional to what a page actually uses.
None of this is a requirement to keep working โ your extension will run fine. But if the community's forums are going to feel the speed-ups in this release, the ecosystem has to pull in the same direction. The core team has done its part; the tooling to do yours is now in your hands. We're happy to help in #extend if you hit something.
๐จ On the small UI changes
You'll also notice a handful of interface touches this cycle: an optional theme switcher in the header, search surfaced in the mobile navigation, a setting to force a single FontAwesome icon style, tidier header behaviour when space runs out, and a few dropdown and layout refinements.
Here's the framing on these: they're representative, not prescriptive. Some you'll like, some you won't โ and that's fine. Flarum has always been deliberately neutral out of the box: a clean, opinion-light foundation rather than a finished look imposed on you. These changes nudge a few defaults; they don't lock anything in.
And that's the whole point of Flarum's design. It is, and always has been, flexible and extensible to its core. Every one of these touches is a default, not a decree โ a theme, an extension, or a few lines of your own CSS can change, replace, or remove any of it. Third-party extensions have always been able to reshape the interface however a community wants, and they always will be. If a new default isn't for you, the ecosystem that lets you override it is right there, exactly as it has been since day one.
So take the UI changes as a sample of the direction โ a reasonable set of defaults โ and remember that "reasonable default" has never meant "the only option" in Flarum, and never will.
๐ก๏ธ The reliability layer
Alongside the speed work, a big batch of fixes โ most of them, again, from people running 2.0 for real and telling us where it bit. A few worth calling out:
- Notification emails no longer route to the wrong queue. They now go through the queue router regardless of provider boot order, so email work lands on its dedicated worker instead of falling through to the default queue and stalling. (#4941)
- The LESS compiler cache survives concurrent writes. Under load, two page-builds racing on the same cache file could corrupt it and throw; the cache is now written atomically and reads defensively. (#4942)
- Case- and accent-variant tag searches don't error anymore โ
Utf8SlugDriver resolves a slug that the database matched under a case-insensitive collation. (#4940)
- The realtime reconnect catch-up stops emptying the discussion list, and a push whose connection was interrupted is retried instead of lost. (#4889, #4903)
- The typing indicator no longer breaks when someone's device clock is off โ typing events are now timed on arrival rather than trusting the sender's clock, so a skewed clock can't leave a user showing as typing forever, or never at all. (#4966)
- The page shell stays rendered while a discussion loads, so navigating no longer flashes an empty frame. (#4881)
- The wide-screen layout was reworked and then tuned โ the header no longer overlaps itself when it runs out of room, desktop containers are clamped to the space available, and the discussion reading width, sidebars and post font were retuned for comfort on large displays. (#4869, #4870/#4905/#4906/#4908/#4909, #4964)
- Mobile search moved off the fixed nav strip into the drawer and the index toolbar, which frees the header and restores the discussion back button's tap target. (#4937, #4968)
- The statistics dashboard reads correctly again โ the date-range dropdown isn't clipped, the previous-period series is populated, and custom ranges count in full. (#4950, #4952, #4956)
Two of the fixes are also security hardening: email values are no longer parsed as markup (#4874), and token model keys are declared as strings to close an integer-coercion weakness (GHSA-55f2-h36g-96c3) (#4935).
๐งฐ New capabilities, if you want them
Not everything this cycle is invisible. A few things are genuinely new โ all opt-in:
- Pausable queues, with a queue dashboard widget that manages failed jobs, plus extension seams so other tools can plug into it. (#4841, #4846, #4849, #4850)
- A
tinker command for an interactive REPL against your forum. (#4829)
- Database version requirements, with recommended-version warnings so you know before you hit a wall. (#4834)
- Isolated migrations, and a setting to sort the discussion list by title โ with tags able to set the sort their list opens with. (#4864, #4921, #4922)
- Links back to the forum now render as the discussion they point at, and relative links are treated as the forum's own. (#4924, #4927)
- An optional theme switcher in the header, letting people flip between light and dark themselves. (#4936)
- A setting to force a single FontAwesome icon style forum-wide, with the icons whose weight carries meaning left untouched. (#4868, and follow-ups #4873/#4878/#4892/#4893)
- An experimental, opt-in CJK search mode for Chinese/Japanese/Korean forums, where the default word-based search can't match partial terms. (#4965)
๐ Made with the community
14 of this cycle's 100+ PRs came from outside the core team. On a release this heavy on internal performance surgery, the outside contributions are the fixes and touches that come from people living in the software day to day โ and every one of them made the release better.
Thanks to everyone who shipped this cycle:
- @Karl - the mobile search/drawer fix, the blank index title control, phone dropdown icon overlaps, the approval fade scope, and repairing dialogs left pointing at a deleted message (#4811, #4821, #4822, #4836, #4851)
- @huoxin233 - letting boolean settings with truthy defaults be disabled, and preventing a deleted fallback for unsynced posts during formatting (#4782, #4824)
- @GianniG - the setting to disable sticky pinning on All Discussions, and handling frontend network-connection loss (#4609, #4854)
- @davetodave178 - running migrations in isolation (#4864)
- @CyberGene - honouring
user.viewLastSeenAt in the realtime typing indicator, and fixing it against skewed device clocks (#4880, #4966)
- @drewmt - teaching PHPStan to recognize conditional model casts, and populating the statistics previous-period series (#4920, #4952)
- @TowyTowy - blocking disabling the default language pack (#4813)
- @IanM - the performance pass, the reliability fixes, the new capabilities, and most of the rest
First PR or fiftieth, it all counts. If you've been meaning to jump in, the water's warm.
๐งญ On the fence? Ask the Upgrade Advisor
For most forums, "should I upgrade?" really means "will my extensions come with me?" โ and there's a free tool that answers it before you touch a thing. FoF Upgrade Advisor adds an admin page that runs a batch of Flarum 2.0 readiness checks and boils them down to a single go/no-go verdict:
- Every installed extension is checked for a 2.0-compatible release โ the thing that most often holds an upgrade up โ with abandoned, superseded, and premium/private cases each called out.
- PHP and database versions are checked against what 2.0 needs (and, now, nudged toward the versions this release recommends).
Install it, read the verdict, clear what it flags:
composer require fof/upgrade-advisor:"*"
Once it reports green and you've made the jump, it's done its job โ uninstall and carry on.
๐ฆ Your feedback decides whether this becomes 2.0
Let's be direct about where we are. rc.6 is a release candidate: the plan is that a release candidate that holds up in the real world becomes 2.0.0 stable, unchanged. We can't make that call from here โ the only thing that tells us rc.6 is ready to be 2.0 is forums actually running it and reporting back. Not the demo, not our own testing: your forum, your extensions, your members, your traffic.
So this is the ask, and it genuinely gates the release:
- Look before you leap โ kick the tyres on the nightly demo or right here on discuss.flarum.org, both tracking
2.x.
- Upgrade a real forum โ
composer update -W, then php flarum migrate, php flarum cache:clear, php flarum assets:publish. Back up first; stage it if you can.
- Report back โ the wins, the weird bits, and anything that breaks.
Help us triage: must-fix-now vs. later
Just as important as reporting problems is helping us weigh them, because not everything found has to be fixed before 2.0 ships. When something comes up, the question we'll be asking is:
- Does it block 2.0? Data loss, a security issue, a broken upgrade path, a regression from the previous release, or something with no workaround โ these are must-fix-now. They hold the stable tag.
- Or can it wait? A rough edge, a nice-to-have, a cosmetic issue, or something an extension or a bit of CSS can work around โ these can land in a 2.0.1 or 2.1 without holding 2.0 back.
Holding 2.0 indefinitely for every last polish item helps no one; shipping 2.0 with a genuine blocker helps no one either. Tell us what you hit and how much it actually hurts, and we'll draw that line together.
Where to send it:
Every real-world upgrade report is a data point on the one question that matters right now: is rc.6 good enough to be 2.0? At this stage, that decision is yours as much as ours. ๐