How to fix ctrl+c inside a docker container
The problem is that Ctrl-C sends a signal to the top-level process inside the container, but that process doesn't necessarily react as you would expect. The top-level process has ID 1 inside the container, which means that it doesn't get the default signal handlers that processes usually have. If the top-level process is a shell, then it can receive the signal through its own handler, but doesn't forward it to the command that is executed within the shell. Details are explained here. In both cases, the docker container acts as if it simply ignores Ctrl-C.
Starting with docker 0.6.5
, you can add -t
to the docker run command, which will attach a pseudo-TTY
. Then you can type Control-C
to detach from the container without terminating it.
If you use -t
and -i
then Control-C will terminate the container. When using -i with -t
then you have to use Control-P Control-Q
to detach without terminating.
Test 1:
$ ID=$(sudo docker run -t -d ubuntu /usr/bin/top -b)
$ sudo docker attach $ID
Control-P Control-Q
$ sudo docker ps
The container is still listed.
Test 2:
$ ID=$(sudo docker run -t -i -d ubuntu /usr/bin/top -b)
$ sudo docker attach $ID
Control-C
$ sudo docker ps
the container is not there (it has been terminated). If you type Control-P
Control-Q
instead of Control-C in the 2nd example, the container would still be running.
Wrap the program with a docker-entrypoint.sh bash script that blocks
the container process and is able to catch ctrl-c. This bash example
might help:
https://rimuhosting.com/knowledgebase/linux/misc/trapping-ctrl-c-in-bash
#!/bin/bash
# trap ctrl-c and call ctrl_c()
trap ctrl_c INT
function ctrl_c() {
echo "** Trapped CTRL-C"
}
for i in `seq 1 5`; do
sleep 1
echo -n "."
done
Docker not responding to CTRL+C in terminal
This post proposes CTRL-Z as a workaround for sending the process to background and then killing the process by its process id:
Cannot kill Python script with Ctrl-C
Possible problems:
The program catches ctrl-c and does nothing, very unlikely.
There are background processes that are not managed correctly. Only the main process receives the signal and sub-processes hang. Very likely what's happening.
Proposed Solution:
Check the programs documentation on how it's properly started and stopped. ctrl-c seems not to be the proper way.
Wrap the program with a docker-entrypoint.sh bash script that blocks the container process and is able to catch ctrl-c. This bash example should help: https://rimuhosting.com/knowledgebase/linux/misc/trapping-ctrl-c-in-bash
After catching ctrl-c invoke the proper shutdown method for ipython notebook.
Unable to stop my Docker container with Ctrl-C
The reason for your problem is that the kernel treats a process with PID 1 specially, and does not, by default, kill the process when receiving the SIGTERM
or SIGINT
signals.
You have two options:
- Add
--init
flag todocker run
command. By that a special process with PID 1 will be created, which will be a parent for your process and will proxy all signals and properly reap your processes. - Add explicit signal handling to your app, which is good if you want to do graceful shutdown.
The good practice is to combine both methods.
Uvicorn won't quit with CTRL+C
You need to run the container with --tty
and --interactice
so that your keyboard presses are forwarded.
-t, --tty Allocate a pseudo-TTY
-i, --interactive Keep STDIN open even if not attached
docker run -ti myapp
Additionally, you should make sure that you don't use shell syntax for your entrypoint, if you have one.
The shell form prevents any CMD or run command line arguments from being used, but has the disadvantage that your ENTRYPOINT will be started as a subcommand of /bin/sh -c, which does not pass signals. This means that the executable will not be the container’s PID 1 - and will not receive Unix signals - so your executable will not receive a SIGTERM from docker stop .
ENTRYPOINT ["uvicorn"]
CMD ["app.py"]
On a side note, closing the terminal, doesn't stop the container, normally. You should still see it running when doing docker ps
.
how to docker run, support Ctrl+C, and not combine stderr
The best solution I found is tini. Specifically, add the following to the Dockerfile
:
ENV TINI_VERSION v0.18.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "-g", "--"]
This is taken mostly verbatim from the tini README, and adds the -g
, which makes Ctrl+C work more like you would expect.
Now there's no need to use --tty
, and no smushing of stdout
and stderr
.
How does it work?
tini
's author does a great job explaining it. But to summarize, docker runs your process as PID 1. PID 1 is normally init, and it has some responsibilities, like handling signals. bash
, and most other programs you might run in your container, don't do those things. tini
does, and it does such a good job, they added it to docker
as --init
. The only problem with that flag is that it doesn't add -g
, which is important for shell scripts. So I recommend adding it to the Dockerfile
manually.
Related Topics
Is There Any Shortcut to Reference the Path of the First Argument in a Mv Command
Any Porting Available of Backtrace for Uclibc
"Cannot Execute Binary File" When Trying to Run a Shell Script on Linux
.Bashrc Not Read When Shell Script Is Invoked from Desktop Shortcut
Linux Run Kernel Probe Systemtap Script Failed with Semantic Error: No Match"
Overlay Two Postscript Files (Command Line Approach)
Why Mongodb Performance Better on Linux Than on Windows
Using Perf Probe to Monitor Performance Stats During a Particular Function
Case-Insensitive Glob on Zsh/Bash
Tcp: Server Sends [Rst, Ack] Immediately After Receiving [Syn] from Client
Rename File Command in Unix with Timestamp
Extract Average Time from Ping -C
Decrypt Obfuscated Perl Script
Automated Test Tools for Linux/Ncurses
Linux - Without Hardware Soundcard, Capture Audio Playback, and Record It to File