Graceful shutdown of NodeJS/Remix Server

Hi! I’m unable to gracefully shutdown a Remix app using the Remix Blues Stack server.ts and Indie Stack start.sh script (workaround suggested by fly.io team for npx prisma migrate deploy release command issues).

The specific issue is that the server never receives the SIGINT signal. I have added the following to the server, which works locally, but is never triggered on deployed VMs.

export function addShutdownHandlers(shutdownFn: NodeJS.SignalsListener) {
  ["SIGINT", "SIGTERM"].forEach((signal) => {
    process.once(signal, shutdownFn);
  });
}

I have verified that a signal is being sent via logs and that it just isn’t be handled/sent to the nodejs running process. I have previously tried adding tini to the Dockerfile and upgrading to yarn:berry, but neither approach worked.

Here are my questions:
1. What is the correct structure for the Dockerfile in order for the CMD/ENTRYPOINT to receive the SIGINT signal?
2. Is there an example NodeJS app/configuration that has graceful shutdown working?


Relevant snippets from files mentioned above

Dockerfile

// ...
ENTRYPOINT [ "./start.sh"]

start.sh

set -ex
npx prisma migrate deploy
yarn start

package.json

"start": "node --require ./tracing.js ./build/server.js",

I don’t know about Remix but in my Node apps I simply do something like:

async function closeGracefully () {
	// do whatever you need to do...
	process.exit();
}

process.once('SIGTERM', closeGracefully);

Which AFAIK works as expected.

In the Docker file I usually start the app with something like this:

CMD ["node", "src/index.js"]

Why would it matter how you start your app in Docker to receive the signals? :thinking:

1 Like