nc -z (Port Check): Usage, Options & Common CI Errors
nc -z probes a TCP port without sending data - the standard CI readiness check.
Waiting for a service's port before running tests is the single most common networking task in CI, and nc -z -w is the idiom for it. The catch: netcat has incompatible variants, and -z behaves differently across them.
What it does
nc -z performs a scan/connect to a port and exits without sending data: exit 0 if the TCP handshake succeeds, non-zero otherwise. Combined with -w (timeout) and a retry loop it is the canonical "wait until the database/API is listening" gate before tests run.
Common usage
nc -z -w 3 localhost 5432 && echo "port open"
# Wait-for-port loop:
until nc -z -w 2 db 5432; do echo "waiting for db"; sleep 1; done
nc -z -v localhost 8080 # verbose: prints open/closed
nc -z -w 5 host 80 || exit 1 # gate the step on the port
# bash builtin alternative (no netcat needed):
timeout 30 bash -c 'until (echo > /dev/tcp/db/5432) 2>/dev/null; do sleep 1; done'Options
| Flag | What it does |
|---|---|
| -z | Zero-I/O: just test the connection, send nothing |
| -w <secs> | Timeout for connect (and overall) |
| -v | Verbose - report open/closed/refused |
| -u | Probe UDP instead of TCP (unreliable check) |
| exit 0 / non-zero | Open / closed - the gate signal |
Common errors in CI
The biggest trap is netcat variants: OpenBSD nc supports -z, the GNU/traditional netcat may not, and BusyBox nc (Alpine) often lacks -z entirely - "nc: invalid option -- 'z'" means a different netcat; use the bash </dev/tcp loop instead, which needs no package. -z on UDP "succeeds" even when nothing listens (UDP is connectionless), so never use nc -zu as a readiness check. Always pair -z with -w so a dropped SYN times out instead of hanging. And "port open" is not "app ready" - the process may be listening before it can serve, so a port check plus an HTTP health check is more reliable.