PM2 is a very popular application used to launch Node.js (and other scripts) in a production fashion.
Although PM2 does a good job of launching process on bare-metal, it is not ideal in a Docker environment.
When a Node.js process exits, it exits with a particular process event:
- SIGINT
- SIGTERM
- SIGKILL
If the Node script is started with npm start
for example, these events may not be properly exposed to the parent process(es) allowing error handling to take place.
Similar scenarios may take place when using pm2 start index.js
within a Docker container. If a SIG*
event is thrown, the event may be swallowed by npm
or pm2
.
PM2 does offer a method for Docker, however there's a better way. Read on!
👉 tini 👈
From the repo:
All Tini does is spawn a single child (Tini is meant to be run in a container), and wait for it to exit all the while reaping zombies and performing signal forwarding.
In this repository are a few sample Dockerfiles --
- Dockerfile: A barebones Dockerfile which supports exposing a Node.js app; common in online tutorials
- Dockerfile-pm2: An advanced Dockerfile with environments and security scans, but is using
pm2
- Dockerfile-tini: A production-ready Dockerfile with environments, security scans and proper
SIG*
handling
Three versions of this image are available to test each scenario:
Base Docker image:
docker run -d -p 8080:8080 --name docker-node-init-base jonfairbanks/docker-node-init:latest --restart=always
PM2 Docker image:
docker run -d -p 8080:8080 --name docker-node-init-pm2 jonfairbanks/docker-node-init:latest-pm2 --restart=always
Tini Docker image:
docker run -d -p 8080:8080 --name docker-node-init-tini jonfairbanks/docker-node-init:latest-tini --restart=always
To test how a theoretical Node.js app runs in these scenarios, this app exposes a few endpoints to test with:
- /: Landing page
- /sigint: Sends the app process a
SIGINT
signal after X seconds - /sigkill: Sends the app process a
SIGKILL
signal after X seconds - /sigterm: Sends the app process a
SIGTERM
signal after X seconds
The following process.env options are supported:
PORT
- Override the application port (Default: 8080)SECS
- Override seconds before process signals are sent (Default: 2)