I think you just need a mail provider and configure a MAIL_URL in your application, @wreiske.
Yeah, I was saying on meteor deploy --free it might be nice to have a “mock” email server that you can go check your mail. Even in local development, instead of just console logging out the email, it could use a nice real-time meteor inbox.
I might work on this. This is something supabase and other projects have with Mailpit - email & SMTP testing tool Mailpit - email & SMTP testing tool
Our ‘Event Loop Delay’ went from 97ms avg ‘(v3.4)’ to 18ms avg ‘(v3.5-beta.4)’ when using Change Streams.
V3.4
V3.5-beta.4
Just pushed an update that will send email to an inbox! BTW… does the --free plan allow testing change streams? https://todo-sample.meteorapp.com
it should support, i’ll double check it today
fixed in the root post
AMAZING! what observability tool are you using to mensure it?
[EDIT] : oh, just realized it’s SkySignal: Modern APM Built for Meteor 3.x
agree 100% with nacho comment and add: please, open a forum topic only for it, let discuss better the usage, love it
x-posting this:
I would love to see this in core at some point!
Having it be able to yield would be
!
Is it open source? Could we see the package?
Better visual indicator of the performance benefits achieved with 3.5-beta.4, the red line is where it was deployed. Change Streams are going to be a scalability gamechanger for Meteor.
Wish me luck… about to migrate a mission critical real-time monitoring app we use to monitor our entire infrastructure. 1.11.1… it hammers CPU and oplog… i’ll let you know how change streams changes it!
This project uses Meteor 1.11.1, which isn't available on this platform. To
work with this app on all supported platforms, use
meteor update --release METEOR@3.4 to pin this app to the newest compatible
release.
Upgrading to 3.5-beta.4!!!
How is the upgrade going? Excited to hear your experience.
Hi,
I am currently testing 3.5-beta.4 and I have issues sur ObjectID.
I have collections with ObjectID fields. When I subscribe to a publication with these king of field, I can see the following in Meteor Dev Tools (DDP):

So, the application has errors.
With 3.4 or 3.5 / "mongo": { "reactivity": ["oplog", "polling"] }, I can see:

and everthing works well.
It was originally a mixture of Blaze / Bootstrap / Ionic… Used meteor user-status to determine who was actively actually looking at the dashboard (bottom left avatars show activity in real-time like the rest of the dashboard)
Well, after 2 days of Opus 4.6 going at it and a little hand holding… it’s completely re-written in Typescript, React 19, TailwindCSS 4 and running smoothly! 1.11.1 to 3.5-beta.4 in 2 days!
This tool was my take on real time infrastructure monitoring before grafana was cool
. Plus… it’s still more real-time than the “auto refresh” you get with tools like grafana/zabbix.
Another successful migration with this workflow:
- Open VS code and start a new workspace
- Go to File - > Add Folder to Workspace
- Add your existing / old meteor project
git clone https://github.com/wreiske/meteor-react-tailwind-prettier-starter project-reactsomewhere on your machine- Go to File - > Add Folder to Workspace → select project-react
- Open Copilot, make sure it’s in “Plan” mode and tell it that there are two projects in the workspace.
Here’s what I used:
There are two projects in the workspace.
statengineandstatengine-react
statengineis an old Meteor 1 application that we need to rewrite into the new modern Meteor 3.4 stack inside ofstatengine-react, which is currently a “TODO example app” that is a “starter” template for React19, TailwindCSS 4, Typescript, etc.Please come up with an extensive plan for how we can handle a complete migration and cleanup of the original statengine code base into the new format.
After a little while it will have a plan. review it and make any edits you think are required… This was my response:
OK. Let’s do it. Start the plan. Please make sure you put the entire plan into a markdown file and as you go through your plan, check off each item, so we know where we left off.
Also, i’d like to use luxon instead of date-fns.
And then hand hold it with a few “continue with the plan…” until it’s complete! ![]()
This workflow has allowed me to migrate several old projects with very little hand holding and my typical “format, lint, commit, push” prompt and copilot instructions to keep code DRY, KISS, etc…
YMMV!
Going to do some serious code review before pushing out to a test cluster node and then I’ll check our CPU / Memory / etc old vs new for a few days and see what improvements we get!
Congrats! The new UI looks great.
Excited to hear what perf shows on 3.5
3.5-beta.6 is out
what’s new?
DDP Session Resumption (#14051)
When a client loses its network connection and reconnects within the grace period (default: 15 seconds), Meteor now resumes the existing DDP session instead of creating a brand new one.
What this means in practice:
- onConnection callbacks are not re-triggered on resume
- The client keeps its original connection ID
- No full session re-initialization and data re-fetch — significantly reducing CPU spikes on reconnect (e.g., after load balancer timeouts on platforms like Google Cloud Run)
- Only ungraceful disconnects (network drops, browser close) are resumable. Intentional disconnects (explicit logout, server kick) are not.
Two new server-side options are available:
Meteor.server.options.disconnectGracePeriod(default: 15000ms)Meteor.server.options.maxMessageQueueLength(default: 100)
Big thanks to @vlasky for the comprehensive implementation and test coverage.
DDPRateLimiter Now Supports Async Rule Matchers (#14182)
DDPRateLimiter rule matchers can now be asynchronous functions, enabling use cases like database lookups inside rate limiting rules, something that wasn’t possible before.
As a bonus, the internal logic was refactored to evaluate matching rules only once instead of twice, making rate limit checks slightly faster when your matchers do async work.
DDPRateLimiter.addRule({
type: 'method',
name: 'sendMessage',
async userId(userId) {
const user = await Meteor.users.findOneAsync(userId);
return user && user.role !== 'admin';
}
}, 10, 1000);
TypeScript type definitions and documentation examples have also been updated to reflect the new async-first approach.
Thanks to @9Morello for this contribution.
Email Warning When Accounts.emailTemplates.from Is Not Set (#14044)
Meteor has historically used Accounts Example no-reply@example.com as a default sender when Accounts.emailTemplates.from is not configured. Since example.com is a reserved domain, most SMTP providers silently reject these emails, making it very hard to debug.
Meteor now logs a clear warning at startup when this default is detected, helping developers catch misconfigured email setups early.
Thanks to @harry97 for tracking down this long-standing pain point.
Node.js 24.14.0 & NPM 11.10.1 (#14176)
Meteor 3.5 ships with Node.js 24.14.0 (LTS) and NPM 11.10.1, bringing all the stability, performance, and security improvements from the Node 24.x line. This has been a long-running effort led by @storyteller, huge thanks for keeping the runtime up to date.
Other PRs and improvments
Other PRs and improvments you can see here
Comming soon
- Mongo Collation Support
- Asyncify client side calls
- uWebSockets support
- Fix for (node:75001) Warning (beta.6)
Big Thanks to Our Contributors
Community contributions are the backbone of Meteor 3.5.
- Contributors: @mvogttech @9Morello @StorytellerCZ @harryadel @vlasky
meteor update --release 3.5-beta.6
Changes to your project's package version selections from updating the release:
accounts-base upgraded from 3.2.0 to 3.2.1-beta350.6
ddp-client upgraded from 3.1.1 to 3.2.0-beta350.6
ddp-rate-limiter upgraded from 1.2.2 to 1.3.0-beta350.6
ddp-server upgraded from 3.1.2 to 3.1.3-beta350.6
ejson upgraded from 1.1.5 to 1.2.0-beta350.6
mongo upgraded from 2.2.0 to 2.3.0-beta350.6
webapp upgraded from 2.1.0 to 2.2.1-beta350.6
meteor npm run start
...
=> Meteor server restarted at: http://localhost:3000/
Your app (or one of its dependencies) is using an outdated JSX transform. Update to the modern JSX transform for faster performance: https://react.dev/link/new-jsx-transform
(node:29885) Warning: The `util._extend` API is deprecated. Please use Object.assign() instead.
Exception while invoking method 'login' TypeError: rateLimiter._findAllMatchingRulesAsync is not a function
at Object.DDPRateLimiter.findAllMatchingRulesAsync (packages/ddp-rate-limiter/ddp-rate-limiter.js:126:67)
at Session.<anonymous> (packages/ddp-server/livedata_server.js:639:46)
at Generator.next (<anonymous>)
at asyncGeneratorStep (/Users/wreiske/prj/bluehive-dot/.meteor/local/build/programs/server/packages/ddp-server.js:255:28)
at _next (/Users/wreiske/prj/bluehive-dot/.meteor/local/build/programs/server/packages/ddp-server.js:273:17)
at /Users/wreiske/prj/bluehive-dot/.meteor/local/build/programs/server/packages/ddp-server.js:278:13
at new Promise (<anonymous>)
at Session.<anonymous> (/Users/wreiske/prj/bluehive-dot/.meteor/local/build/programs/server/packages/ddp-server.js:270:16)
at Session.method (packages/ddp-server/livedata_server.js:675:5)
at runHandlers (packages/ddp-server/livedata_server.js:463:58)
at processNext (packages/ddp-server/livedata_server.js:480:7)
at Session.processMessage (packages/ddp-server/livedata_server.js:483:5)
at SockJSConnection.<anonymous> (packages/ddp-server/livedata_server.js:1380:31)
at SockJSConnection.emit (node:events:508:28)
at SockJSConnection.emit (node:domain:489:12)
at Session.didMessage (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/sockjs/lib/transport.js:246:25)
at WebSocketReceiver.didMessage (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/sockjs/lib/trans-websocket.js:104:39)
at WebSocket.<anonymous> (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/sockjs/lib/trans-websocket.js:70:24)
at WebSocket.emit (node:events:508:28)
at WebSocket.emit (node:domain:489:12)
at WebSocket.dispatchEvent (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/faye-websocket/lib/faye/websocket/api/event_target.js:26:10)
at WebSocket._receiveMessage (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/faye-websocket/lib/faye/websocket/api.js:154:10)
meteor://💻app/packages/ddp-rate-limiter.js:126
DDPRateLimiter.findAllMatchingRulesAsync = (input)=>rateLimiter._findAllMatchingRulesAsync(input);
^
TypeError: rateLimiter._findAllMatchingRulesAsync is not a function
at Object.DDPRateLimiter.findAllMatchingRulesAsync (packages/ddp-rate-limiter/ddp-rate-limiter.js:126:67)
at Session.<anonymous> (packages/ddp-server/livedata_server.js:533:44)
at Generator.next (<anonymous>)
at asyncGeneratorStep (/Users/wreiske/prj/bluehive-dot/.meteor/local/build/programs/server/packages/ddp-server.js:255:28)
at _next (/Users/wreiske/prj/bluehive-dot/.meteor/local/build/programs/server/packages/ddp-server.js:273:17)
at /Users/wreiske/prj/bluehive-dot/.meteor/local/build/programs/server/packages/ddp-server.js:278:13
at new Promise (<anonymous>)
at Session.<anonymous> (/Users/wreiske/prj/bluehive-dot/.meteor/local/build/programs/server/packages/ddp-server.js:270:16)
at Session.sub (packages/ddp-server/livedata_server.js:554:5)
at runHandlers (packages/ddp-server/livedata_server.js:463:58)
at processNext (packages/ddp-server/livedata_server.js:480:7)
at Session.processMessage (packages/ddp-server/livedata_server.js:483:5)
at SockJSConnection.<anonymous> (packages/ddp-server/livedata_server.js:1380:31)
at SockJSConnection.emit (node:events:508:28)
at SockJSConnection.emit (node:domain:489:12)
at Session.didMessage (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/sockjs/lib/transport.js:246:25)
at WebSocketReceiver.didMessage (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/sockjs/lib/trans-websocket.js:104:39)
at WebSocket.<anonymous> (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/sockjs/lib/trans-websocket.js:70:24)
at WebSocket.emit (node:events:508:28)
at WebSocket.emit (node:domain:489:12)
at WebSocket.dispatchEvent (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/faye-websocket/lib/faye/websocket/api/event_target.js:26:10)
at WebSocket._receiveMessage (/Users/wreiske/.meteor/packages/ddp-server/.3.1.3-beta350.6.1qdn50ytdgj++os+web.browser+web.browser.legacy+web.cordova/npm/node_modules/faye-websocket/lib/faye/websocket/api.js:154:10)
Node.js v24.14.0
=> Exited with code: 1
=> Your application is crashing. Waiting for file change.
downgrading to 3.5-beta.4 solves the issue.
great catch, i’ll merge a few prs and fix it tomorrow in a beta.7 ![]()
3.5-beta.7 is out
what’s new?
-
DISABLE_SOCKJS=1fully functional end-to-end (client + server), PR #14206- PR #12007 (Meteor 2.7.2) introduced the
DISABLE_SOCKJSenv var but only implemented the client side. The server kept creating a SockJS server regardless. - This PR completes the implementation: when
DISABLE_SOCKJS=1is set, the server now uses native WebSocket instead of SockJS. - Performance gains: warm browser reload is ~5x faster with
DISABLE_SOCKJS=1, eliminates the/sockjs/info?t=...XHR (fired on every connection) and the SockJS dynamic chunk (~56 KB).
- PR #12007 (Meteor 2.7.2) introduced the
-
ddp-client: fix default DDP connection URL for mirror domains, PR #14189- Without this change, mirror domains could all end up connecting to a single WebSocket endpoint derived from
Meteor.absoluteUrl()instead of the specific domain the user opened. - The fix uses the protocol from
Meteor.absoluteUrl()but takes the host (with port) fromwindow.location.host. - No behavior change for apps that already set
DDP_DEFAULT_CONNECTION_URL.
- Without this change, mirror domains could all end up connecting to a single WebSocket endpoint derived from
Big Thanks to Our Contributors
Community contributions are the backbone of Meteor 3.5.
- Contributors: @dupontbertrand @alextaaa @mvogttech
I’m guide glad as to how 3.5 is turning out but are you sure you’re not lumping way too many changes? I thought you’d keep change streams as the only major change
Maybe yes, but we have tons of PRs to merge
, many still from last hacketoberfest, 3.4.1 already has ~25 PR merged, and we will move another ~25 PR to 3.5.1 lol
So I’m trying to concentrate all DDP PRs in 3.5, it’s a few prs but is everything about the same module
We will have a beta version for 3.4.1, the official 3.4.1, so I think we have enough time to experiment with 3.5.






