Production-ready Crystal-lang applications running in Docker containers must correctly process several signals to ensure proper shutdown. Docker provides two main commands to stop a running container: docker stop
and docker kill
. The docker stop
command first sends a SIGTERM signal to the container’s main process, allowing it to perform a graceful shutdown. If the process does not terminate within a grace period, Docker then sends a SIGKILL signal to forcibly terminate it.
This article demonstrates how to handle SIGINT (Ctrl-C), SIGTERM, and SIGKILL signals within a Crystal-lang application.
Download Example Code
The example code is part of my sandbox repository and can be downloaded here.
If you are not familiar with Crystal-lang, the main file containing the business logic for this example is src/signal_trap.cr
.
Installation
The example is fully Dockerized. Ensure Docker is installed, then clone the Git repository.
Usage Instructions
To run the application in development mode:
docker build --pull --force-rm \
-t crystal-signal-trap \
--file ./Dockerfile . \
&& docker run -ti --rm --name=trap crystal-signal-trap
More importantly, to test signal trapping in a production-like environment (using a minimal base image such as scratch, BusyBox, or Alpine), run the following:
docker build --pull --force-rm \
-t crystal-signal-trap \
--file ./Dockerfile-production . \
&& docker run -ti --rm --name=trap crystal-signal-trap
Demo
Processing Signals Individually
Instead of grouping signal traps as demonstrated in the example application’s code, it is possible to handle each signal separately:
Signal::INT.trap do
# Processing SIGING
end
Handling Other Signals
Docker allows sending various signals to containers:
docker kill --signal=SIGHUP my_container
In such cases, the Crystal-lang application should trap the specific signal, for example, Signal::HUP.trap { ... }
for SIGHUP. A comprehensive list of signals known to Crystal-lang is available in the API documentation.
References: