While auditing one of my own packages I noticed this line in journalctl -u docker, once for every update of that app:
Container failed to exit within 10s of signal 15 - using the force
My start.sh ended with exec gosu cloudron:cloudron python3 server.py, so the app itself was PID 1, and PID 1 only receives SIGTERM if it installs a handler for it. To be clear upfront: nothing broke. The update takes 10 seconds longer and requests that are in flight get cut off, and for an app with SQLite in WAL mode that is harmless. I'm only posting because it turned out not to be limited to my own packages.
I then looked at how long the stop takes for every app on my three servers (Cloudron 10.0.5), using the time between stopContainer and deleteContainer in the app task logs since 1 September:
App
PID 1
Stops that took 10 s or more
Surfer (4 installs)
node /app/code/server.js …
16 of 16
MiroTalk SFU (3 installs)
npm start
12 of 12
IP2Location
node /app/code/index.js
4 of 4
Release Bell
node /app/code/index.js
2 of 2
For comparison, the apps with apache2 or supervisord as PID 1 (WordPress, Nextcloud, FreeScout, EspoCRM) stop in under 4 seconds, and so does another app that runs plain node as PID 1 but apparently handles the signal itself. A throwaway container from the Surfer image shows the same thing in isolation: plain node as PID 1 takes 10.1 s for docker stop, with a process.on('SIGTERM') handler 0.1 s, and with docker run --init also 0.1 s.
In my own two packages I changed the last line of start.sh to exec /usr/bin/tini -- gosu cloudron:cloudron python3 server.py (tini is already in cloudron/base). The stop went from 10.2 s to 0.2 s and the line is gone from the docker log.
Is this something you'd rather handle per package, or is the hard kill simply fine for these apps? I can imagine it matters little for something like Surfer, I was mostly surprised to see it in the log.