🚀 Meteor Scaling/Performance Best Practices

I have a lot to say. Consider this post to be part 1.

Scaling/performance tips that DO NOT require changes to your code

  1. Use a dedicated server instead of a VPS. I refer to my previous post An Enemy of Scalability - Hypervisor (Virtualization) Overhead.

  2. If you are doing lots of file I/O or if you are making heavy use of the database, make sure your dedicated server has an SSD, preferably an NVMe SSD. Hetzner and OVH offer dedicated servers with NVMe SSDs. There are a few other providers as well.

  3. Use Nginx as a reverse-proxy in front of your Meteor app. Nginx will do what it is most efficient at - terminating your HTTPS connection and serving static assets. You want to avoid the node process having its time needlessly wasted.

    Our Meteor deployment script also compresses static assets on disk using the brotli compressor. Nginx’s brotli module has an option brotli_static that enables Nginx to automatically serve the compressed version of assets from disk by looking for files with the .brotli extension instead of having to waste its CPU time compressing the asset on-the-fly.

  4. If you can, make all subsystems running on the same server communicate with each other using UNIX sockets instead of TCP sockets. Communication over UNIX sockets incurs significantly less overhead.

    In our deployments, Nginx passes requests onto Meteor via its UNIX socket file (e.g. /var/lib/mysql/mysql.sock) and Meteor passes requests onto MySQL via its UNIX socket (e.g. /var/run/meteor/meteor.sock).

    To configure Meteor to listen on a UNIX socket, specify the UNIX_SOCKET_PATH environment variable.

    Other common services like MongoDB, PostgreSQL, Redis and Memcached can be configured to listen on UNIX sockets as well.

  5. Cloudflare supports proxying WebSockets so it works well with Meteor apps. It is useful for:

  • Preventing the IP address of your origin server from being exposed,

  • Providing some protection against DDOS attacks

  • Serving static assets from Cloudflare’s edge locations closest to the user and reducing your origin server’s data usage.

  • If you have a server outage, you can use Cloudflare’s API to quickly divert traffic to a hot standby Meteor app server without any DNS propagation delay

  • Cloudflare also offer a load balancer if you have a cluster of Meteor app servers and need to distribute traffic between them.

    If you are using Cloudflare with Meteor, you should modify your Meteor startup script to set the environment variable HTTP_FORWARDED_COUNT=1

    If you are using Cloudflare with Nginx and Meteor, you should modify it to set HTTP_FORWARDED_COUNT=2

Scaling/performance tips that DO require changes to your code

  1. Do everything you can to avoid running CPU intensive code in the Node.js event loop. Instead, such code should run asynchronously in the thread pool.

    Up until recently, this often had to be done by writing a Node Addon in C++ using Native Abstractions for Node (NaN) or N-API. This approach is commonly used by number-crunching code that has to run at top speed, e.g. high performance crypto packages like bcrypt and shacrypt.

    However, thanks to the introduction of the node.js worker_threads module combined with the SharedArrayBuffer data type, it has become more practicable to write thread pool code in JavaScript and get acceptable performance.

    For more info:

    There is also node’s inbuilt cluster module that allows you to spawn multiple node.js (Meteor) worker processes. It has its uses and I have commented on it in the past. Today, I would advise people to first try and solve their problems using the thread pool and only use the cluster module as a last resort.

27 Likes