Skip to content

Proposal: handle SIGTERM by default to initiate graceful worker shutdown #3135

Open

Description

@seanbollin

Summary

A Temporal Java worker installs no graceful-shutdown signal handling by default. The JVM runs shutdown hooks on SIGTERM, but the SDK registers none that drain the worker, so a bare worker exits without stopping polling or draining in-flight work. In-flight activities are abandoned and retried server-side after their timeouts.

Proposal: consider a built-in option to initiate WorkerFactory shutdown on SIGTERM by default (opt-out-able), so the common containerized case — stop polling → drain → cancel — works without extra wiring.

Current workaround

A JVM shutdown hook calling WorkerFactory.shutdown() → awaitTermination(timeout) → shutdownNow() (plus WorkerOptions.setAllowActivityHeartbeatDuringShutdown(true) to let a heartbeating activity finish during the drain); or run under the Spring Boot starter, which wires it.

Precedent

The TypeScript SDK already does this: its Runtime installs handlers for SIGINT/SIGTERM/SIGQUIT/SIGUSR2 by default and calls shutdown() on every worker.

Trade-offs / open questions (why it is not already the default)

  • A worker usually runs inside a larger process (HTTP server, DI host, other lifecycle). A library installing a process-global signal handler can conflict with the host application's own signal handling, so any default must be clearly opt-out-able and should no-op when a host framework already owns the signal.
  • Default-on vs. an explicit opt-in flag?
  • If added, match the TypeScript semantics and honor the existing grace/drain config.

Why this comes up

SIGTERM-before-SIGKILL is the standard stop protocol for Kubernetes, Cloud Run, ECS, docker stop, and systemd. Users commonly expect a worker to drain on SIGTERM without extra wiring, and it is a recurring support question (e.g. https://community.temporal.io/t/proper-way-to-shutdown-on-sigterm-sigint/5485).

Related: #2026.

One of a set of sibling issues on default SIGTERM handling across the SDKs that do not auto-handle it today (Go, Python, Java, .NET, Ruby, Rust). TypeScript already does; PHP delegates process lifecycle to RoadRunner.

Activity

  1. Quinn-With-Two-Ns commented on Oct 8, 2026

    @Quinn-With-Two-Ns
    Contributor

    This would very likely need to be opt-in given that it is not the current behaviour. Given TypeScript is the only SDK that seems to do this by default this discussion is relevant for all SDKs and not just Java so we should discuss somewhere more visible.

  2. self-assigned this
    on Oct 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions