Skip to content

Fix Docker container hanging after one-shot commands - #2280

Merged
sk-keeper merged 1 commit into
releasefrom
fix/docker-entrypoint-oneshot-exit
Aug 11, 2026
Merged

Fix Docker container hanging after one-shot commands#2280
sk-keeper merged 1 commit into
releasefrom
fix/docker-entrypoint-oneshot-exit

Conversation

@maksimu

@maksimu maksimu commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Every authentication branch in docker-entrypoint.sh ended with an unconditional sleep infinity, so a container invoked with a one-shot command printed its result and then never exited. Replaced the eight unconditional calls with a single lifecycle decision: stay resident only for service mode, for an invocation with no command, or when KEEPER_KEEP_ALIVE is set; otherwise exit with the wrapped command's status.

Three further problems in the same lifecycle code:

  • The container ignored SIGTERM. Bash defers trap handlers while a foreground child runs, so sleep infinity kept the existing EXIT INT TERM trap from ever firing. docker stop waited out the full grace period and then SIGKILLed (exit 137), leaving the KSM config monitor uncleaned. Now idles via a backgrounded sleep and wait, with explicit TERM/INT handlers.
  • Arguments containing spaces were split. Command args were flattened into a string and re-split by word splitting, so get "My Record Title" reached Commander as three arguments. Now carried in an array.
  • The KSM one-shot path started a perpetual config monitor. It now uploads config.json once, so refreshed device state still persists, then exits.

Context

Reported in #2264: docker run ... keeper/commander get --format password <uid> prints the password, then hangs forever, making the image unusable in scripts because the exit status never propagates.

Note that before this change only failing commands exited, and then only incidentally via set -e aborting before sleep infinity — so a valid record hung while an invalid one exited.

Behavior verified before/after, with a stubbed keeper.py to exercise the lifecycle logic and against the built image for the end-to-end case:

before after
one-shot command hangs forever exits 0
failing command hung on success only status propagates
get "My Record Title" split into 3 args passed intact
no command given stays alive stays alive
service-create stays alive stays alive
docker stop full grace then SIGKILL (137), no cleanup immediate, 143, cleanup runs

Fixes #2264

Every authentication branch in the entrypoint ended with an unconditional
`sleep infinity`, so a container invoked with a one-shot command printed its
result and then never exited. That made the image unusable from scripts, since
the command's exit status never propagated, and there was no way to opt out.
Only failing commands exited, and then only incidentally, via `set -e`.

Replace the eight unconditional `sleep infinity` calls with a single lifecycle
decision: stay resident only for service mode, for an invocation with no
command, or when KEEPER_KEEP_ALIVE is set; otherwise exit with the wrapped
command's status.

Three further problems in the same lifecycle code:

- The container ignored SIGTERM. Bash defers trap handlers while a foreground
  child runs, so `sleep infinity` kept the existing EXIT/INT/TERM trap from
  ever firing: `docker stop` waited out the full grace period and then
  SIGKILLed, leaving the KSM config monitor uncleaned. Idle via a backgrounded
  sleep and `wait`, with explicit TERM/INT handlers.

- Command arguments were flattened into a single string and re-split by word
  splitting, so any argument containing spaces (record titles, notes, search
  queries) reached Commander as several arguments. Carry them in an array.

- The KSM one-shot path no longer starts the perpetual config monitor. It
  uploads config.json once so refreshed device state persists, then exits.

Fixes #2264
@sk-keeper
sk-keeper merged commit 3419934 into release Aug 11, 2026
4 checks passed
@sk-keeper
sk-keeper deleted the fix/docker-entrypoint-oneshot-exit branch August 11, 2026 05:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants