PM2: Production Process Manager with a Built-In Load Balancer
github.com
github.com
Writing this at 1:30am, might not be very insightful.
We actually are phasing out the use of pm2 in our org since we are already running within kubernetes. And running pm2 within k8s seemed redundant (and is likely a relict of previous deployment strategies).
It also made certain debugging tasks trickier as we very rarely like to attach debuggers to our staging environment and the cluster/fork behavior of pm2 made it hard to effectively target indivual executions/processes.
Further we had some issues running nextjs after upgrading to the latest version within pm2 when using custom configuration files (we did not end up finding the root cause but decided to drop pm2, least path of resistance etc).
One other thing that also gave us headaches was the fact that pm2 allows specifying different env variable values for different environments but no other configuration (such as cluster count etc.)
We were successful entirely replacing it with docker-compose configs locally that are ultimately closer to how our services are deployed in production.
Overall I think pm2 is suitable for local dev and prototyping small to medium complexity services.
I use it to avoid the complexity of those things since my operational needs are usually very modest.
Docker/kubernetes is fine for things that make money, but for small personal projects it is far too much complexity and I dumped it. pm2 gets the job done with less effort overall.
Why did you prefer pm2?
If you’re sat in an office, you’re on a work computer, you need reproducible dev environments, probably none of that bothers you.
However, if I’m working by myself, I don’t want to deal with any of that Rube Goldberg shit. I use shell scripts, ssh, rsync and pretend it’s the 00s again, and oh my god I’m so much happier in that universe.
It saved me from the Rube Goldberg esque pile of bash scripts and puppeteer configs of the 00s.
Funny how perception changes.
The other problem for me is, let’s say you’re in a hotel with crappy wifi and docker wants to download a large quantity of images, you’re in for a rough time.
There’s a very sharp downwards trajectory of user experience once you leave the happy path.
A well crafted image may add an extra 500MBs on top of that. By no means minimal, but not really that bad. (Assuming you have no base images like python or Ubuntu already on your system)
And idk what you mean by “happy path” for docker. Every dockerfile is a snowflake. It’s not a framework like rails.
If you have a good understanding of the tool you shouldn’t have any trouble.
I'm using runit instead of pm2. Systemd should also do fine. It's over engineered.
https://learn.microsoft.com/en-us/windows/wsl/compare-versio...
It’s been a long time.
I get to rsync my code up to a server, & pm2 restart it. The server process runs like any other, no container issues to consider, just a json file saying how I'd run it myself if I wanted to
tbf I'd rather replace pm2 with systemd, since I'm using it to run a Rust program, but if I was using node their cluster mode looks neat
You may already have your own in-app log rotation before adding PM2 so may want to avoid printing to stdout/stderr. For usage with docker you want pm2-runtime.
For small projects, there is now a way to run docker containers or pods (through podman) via systemd units, so if you are using a systemd enabled distro, then that may be a better alternative than pm2, especially if you already have proficiency in docker and kubernetes.
I'm sure there are alternatives, lighter-weight and closer to the metal, but I haven't had the need. PM2 does the job.
People complaining that Node.js only use one CPU core, or benchmarks with low scores for Node.js. There are solutions for this! PM2 is one of them and is very good. Use it.
At work, most of our app is docker-composed in one VPS (early B2B product with few customers). I would like to have the multiple-process benefits of PM2, but I don’t want to spin up a whole load balancer for it, or split the node service from the other services. Everything is so simple right now and I’d like to keep it that way lol.
Last time I googled this I found conflicting opinions, but if I can just stuff PM2 into my node container, I’d be a happy dev!
I can see why people like pm2, similarly to why I can see why people like Nodejs. But I think pm2 is half-baked, and Nodejs's greatest design feature (single-threaded async execution) is flawed by design.
You write nodemon or npm start- and so it's put at the end of the Dockerfile as such.
I know someone that discovered pm2 because copilot autocompleted their Dockerfile that way.
Properly written node apps are bound by I/O throughput. CPU and memory cost for additional processes is minimal, but latency is significantly decreased with a worker pool.
Does that mean that node apps shouldn’t do any computation on data, just move bytes between a file and a network socket? If I need my program to think, I wrote it wrong or in the wrong language?
I would rather have shared memory multithreading be a usable feature in my program’s runtime. SharedArrayBuffer exists but 98% of existing code uses objects and arrays so can’t be easily shared between processes without copying, and the cost of copying objects with structuredClone puts a huge optimization barrier in between most code and parallel processing.
This is a very uncharitable interpretation of Node's primary use case. I think it would be more accurate to say don't use Node if all of the following apply:
- Your task is CPU bound rather than IO bound (e.g. you're not waiting to insert or retrieve data from the database).
- Your task is highly parallel (some computations are sequential and linear).
You can't use worker threads because:
- You have too much data to bear the cost of message passing via the structured clone algorithm.
- You can't share memory and avoid structured cloning because your data can't be represented using an array buffer.
This is a far cry from the claim that Node can't handle problems that require "thinking". When you're on the JIT compiler's happy path Node can think pretty quickly actually. In practice, many high volume web servers are handling enough requests that there's more than enough jobs for each Node process to have exclusivity over its jobs, and you don't need to share work between processes. Each process or worker thread can be largely independent. Not every task is going to fall within those boundaries, but that's ok, Node doesn't have to fulfill every use case.
If you need to compute something complicated, write it in a compiled language and make it a node module. Many popular npm libraries are exactly this.
I thought process managers like supervisor and pm and the others had all been superseded by systemd, unless you’re in a non systemd distro of course.
Systemd won’t do load balancing, but you should have nginx/caddy in front of Node anyway, and either will handle load balancing just fine.
Realistically if your prod environment relies on you manually running `pm2... ` or whatever, you're doing something wrong. User Experience should have about 0% impact on how your service daemons run.
why?
> Docker Swarm mode is built into the Docker Engine. Do not confuse Docker Swarm mode with Docker Classic Swarm which is no longer actively developed.
however the sad thing is currently swarm is held by other company (mirantis) after being sold by docker inc. and the new company doesn't seem that invested in improving swarm. you could say its in life support now save for a few community contribution
more explanation by dockerswarm.rocks website: https://dockerswarm.rocks/swarm-or-kubernetes/
Also, `pm2 flush` doesn't actually truncate all logs, just the logs of whatever processes pm2 happens to be running at that moment.
I workaround this by killing pm2 entirely and `rm -Rf .pm2` once a month or so.
(This is all on Linux BTW.)
Dokku itself doesn't run anything in resident (there is a small go binary that listens to the docker event stream and restarts apps as necessary but thats it), so there shouldn't be overhead there.
On the Docker side, there is a _slight_ amount of overhead in cpu/networking, but its so negligible that its sometimes difficult to benchmark. While I can't really make changes to Docker to alleviate issues, I'd be interested in knowing what parts of Docker are heavyweight so we can better surface any such information to Dokku users.
If I did it all over again, I'd pick something proper like systemd or k8s. I would say this is more a surprisingly capable dev tool than a real service manager.
Take a look at the code. E.g. https://github.com/Unitech/pm2/blob/master/lib/API.js
I don't want this thing running my production environment if I can help it.
Nowadays, docker compose or k8s ecosystems do a lot of this stuff in a more team-friendly & ecosystem-supported manner. I always liked pm2 bc the dynamic API layers... except docker & k8s also have APIs, and you get all sorts of extras with them by being a layer below
I wonder if there is a name for this waller garden phenomena. The pm2 team built a wonderful universe and it keeps improving afaict, but tech ecosystems also have been improving around them and at faster & bigger levels...
Fwiw, last time I asked myself this question my conclusion was that there was no additional value, and we opted to not use PM2. This was a long time ago however, so take it with a grain of salt.
Also, kubernetes cluster workers can fail. How do you want your workload to behave in that case? When your container is killed, should these services fail together? Do you want a better separation of concerns between your app dev team and your devops/middleware team (think disaster recovery and cluster maintenance)? Where does the app dev end and the devops begin? For other languages such as Java or C++, it would be out of the question to let these details matter to anyone but app devs. Their entire apps are in one docker image. Kubernetes should only manage instances of the app and the obvious dependencies that should be externalized (redis, memcached, postgres, mysql, etc.)
In my experience it's really a question of failure modes in your architecture and squeezing out small gains in performance that matter at scale.
Can you make Kubernetes deployments easier than this?
cd myapp
# Ignore multer.
NODE_ENV=production PORT=80 HTTPS_PORT=443 pm2 start bin/www --name myapp --watch --ignore-watch="\.git node_modules uploads"
pm2 startup
pm2 saveIf it happens again I'll likely look into doing it the old fashioned way with systemd.
Skill issue? Most certainly. It still sucks to think I've figured it out (to the point of testing with manual reboots) only to one day have nothing running and have no clue why.
The node cluster module needs a way to provide your own balancing algorithm. Then I wouldn’t feel the need to put a reverse proxy in front of it.
Its more lightweight than spinning up Docker containers for each process
You can come up with a Unix solution by using bash, piping to logs, daemonizing with &.
And as a bonus, it works for any program, not just with node stuff.
Remember, use the tools available in your foundation before adding more blocks to the pyramid
Pm2 is neat, but the monitor solution is pricey.