Shutdown
[server]
shutdown_grace_period_secs = 30 # wait for work in progress, all doors
docker stop -t 90 craft-file-gate # Docker
# Kubernetes: terminationGracePeriodSeconds: 90
The steps
SIGTERM or SIGINT (Ctrl-C) trigger the shutdown, even when the server is PID 1.
| Step | What happens | Bound |
|---|---|---|
| 1 | the SFTP door stops accepting; /readyz answers 503; no password check is admitted any more | - |
| 2 | the ban lists ([sftp.ban], [api.ban], [admin.ban]) are written to their persist_file | 5 s per list |
| 3 | server shutting down sent to every SFTP session; the admin/API door stops accepting and closes its streams | - |
| 4 | sessions without a transfer in progress are cut | - |
| 5 | wait for transfers in progress, if any; ends with the last one | shutdown_grace_period_secs |
| 6 | any session still there is cut | - |
| 7 | wait for the cut sessions to end (spans, last audit lines) | rest of the delay |
| 8 | wait for REST and admin requests in progress | rest of the delay |
| 9 | S3 multipart uploads still open are aborted | 5 s |
| 10 | quota cleanups and upload lock removals | 5 s |
| 11 | last OTLP export (metrics, spans) | 5 s |
| 12 | graceful shutdown complete, exit 0 | - |
Steps 5, 7 and 8 share a single delay, counted from the announcement. Without a transfer in progress, the shutdown does not wait for it.
Tuning the orchestrator
The worst case of a shutdown:
| Component | Default |
|---|---|
shutdown_grace_period_secs | 30 s |
| ban writes | 5 s per list ([sftp.ban], [api.ban], [admin.ban]) |
| backend work | 5 s |
| cleanups and locks | 5 s |
| OTLP export | 5 s |
| total | 50 s with one ban list, 60 s with all three |
The orchestrator’s shutdown delay must exceed it: docker stop -t 90,
terminationGracePeriodSeconds: 90, which the chart sets (grace period plus 60 s). A SIGKILL before the end cuts
transfers without an audit line, leaves S3 multipart uploads open and loses
unexported spans.
What you will see
Each step writes its INFO line: shutdown signal received,
ban files written out before the shutdown sequence, idle sessions disconnected (idle_kicked), waiting for active transfers to complete
(only if there are any), remaining sessions disconnected (force_kicked,
0 included), graceful shutdown complete. Cut sessions end
with session_end reason=shutdown_idle (step 4) or shutdown (step 6).