Stop containers gracefully before remove instead of immediate SIGKILL.
Release rolling / release (push) Successful in 8m31s

This commit is contained in:
Raven Scott
2026-07-15 22:56:15 -04:00
parent bca951aa10
commit afc10d1dbc
+31 -10
View File
@@ -27,10 +27,11 @@ function isNoSuchContainer(err) {
}
/**
* Release attachments that can delay Docker force-remove, then delete.
* Release attachments that can delay Docker remove, stop the container, then delete.
* Always stops first so processes shut down cleanly before removal.
* @param {string} id
* @param {import('../rpc/session.js').PeerSession} session
* @param {{ force?: boolean, v?: boolean, removeVolumes?: boolean, link?: boolean }} args
* @param {{ force?: boolean, v?: boolean, removeVolumes?: boolean, link?: boolean, timeout?: number, t?: number }} args
*/
async function forceRemoveContainer(id, session, args = {}) {
session._cleanupLogsForContainer?.(id)
@@ -49,14 +50,34 @@ async function forceRemoveContainer(id, session, args = {}) {
}
if (args.link) removeOpts.link = true
// SIGKILL first so remove does not wait on a stuck PID / long stop period
if (force) {
try {
await container.kill({ signal: 'SIGKILL' })
} catch (err) {
// not running / already dead — fine
if (!isNoSuchContainer(err) && !/is not running|already stopped/i.test(String(err?.message || ''))) {
logger.debug('pre-remove kill skipped', { id: id.slice(0, 12), error: err?.message })
// Stop first so the container shuts down cleanly before remove
const stopTimeout =
Number(args.timeout) >= 0
? Number(args.timeout)
: Number(args.t) >= 0
? Number(args.t)
: 10
try {
await container.stop({ t: stopTimeout })
} catch (err) {
// not running / already dead — fine
if (!isNoSuchContainer(err) && !/is not running|already stopped/i.test(String(err?.message || ''))) {
logger.debug('pre-remove stop skipped', { id: id.slice(0, 12), error: err?.message })
// If force and stop failed unexpectedly, fall back to SIGKILL so remove can proceed
if (force) {
try {
await container.kill({ signal: 'SIGKILL' })
} catch (killErr) {
if (
!isNoSuchContainer(killErr) &&
!/is not running|already stopped/i.test(String(killErr?.message || ''))
) {
logger.debug('pre-remove kill skipped', {
id: id.slice(0, 12),
error: killErr?.message,
})
}
}
}
}
}