wait-for-it.sh reports success even when ports never open
I wasted an entire afternoon debugging a database image because my entrypoint script told me everything was fine when it actually wasn't. I was using the standard setup in my script:
./wait-for-it.sh db:5432 -- python app.py
The container exited with a 0, but the app crashed three seconds later with a connection refused error. It turns out the database never actually started. The wait-for-it.sh script knew this—it even printed a timeout warning—but it executed my command anyway. Since it used exec, the final exit code I saw was from python app.py starting up successfully for a few seconds, completely masking the fact that the wait had failed.
Why the script executes regardless of failure
This isn't a bug, but a design choice that contradicts what the tool's name implies. Looking at the 182-line bash file in the Raknaos/wait-for-it repo, the final logic block looks like this:
if [[ $WAITFORIT_CLI != "" ]]; then
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
exit $WAITFORIT_RESULT
fi
exec "${WAITFORIT_CLI[@]}"
else
exit $WAITFORIT_RESULT
fi
The script only refuses to run the subprocess if both the wait fails and the -s flag is present. Without -s, a failed wait just falls through to the exec command. Because exec replaces the script process with the child process, the timeout error vanishes from the return code.
I verified this with two tests on my machine:
- Test 1:
./wait-for-it.sh 127.0.0.1:1 -t 5resulted in a timeout and an exit code of 124 (which is the coreutilstimeoutstatus leaking through). - Test 2:
./wait-for-it.sh 127.0.0.1:1 -t 2 -- echo "RAN ANYWAY"printed the timeout warning, but then printed "RAN ANYWAY" and exited with 0.
wait-for-it.sh without the -s flag isn't a gate; it's just a delay with a maximum ceiling. If you actually need to block execution until a port is open, the -s flag is mandatory.
The logic behind using this over a custom rewrite
I stuck with this repo instead of writing my own wrapper because the way it handles the probe is actually quite elegant:
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
nc -z $WAITFORIT_HOST $WAITFORIT_PORT
WAITFORIT_result=$?
else
(echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
WAITFORIT_result=$?
fi
It leverages /dev/tcp, a bash pseudo-device. This allows the script to work in scratch containers that have bash but lack curl, netcat, or Python. It also handles Alpine's busybox implementation of timeout by using realpath to check if the binary is actually busybox. Handling those environment differences in just a few lines of pure bash is why this tool is useful.
The Raknaos revival of the original vishnubob script also includes a test/ directory with wait-for-it.py, container-runners.py, and a requirements.txt that pins docker>=4.0.0 and parameterized>=0.7.0. These tests run across Debian-python and Alpine/busybox images to ensure the busybox branch works correctly, which is a nice addition to a tool designed to have zero external dependencies.
Finally! I'm tired of manual sorting. Does this work with 7-Zip or just standard folders?
Confused. Why are you talking about 7-Zip in a shell script thread? Maybe try using fzf instead...