nc is three programs sharing one name, and the option table you find usually belongs to the wrong one.
On Ubuntu and Debian the nc in your PATH is netcat-openbsd, which dropped the flags that netcat-traditional and Nmap’s ncat kept, so a line copied from a guide about either of those fails the moment you run it. I ran every command below against the nc that ships with Ubuntu 24.04, including the ones that error out, so you can see the output before you paste anything into a shell.
Which netcat you actually have
Netcat began as one program in 1996 and now survives as three separate codebases that share the name. Each accepts a different set of flags, so the first job is finding out which binary your nc resolves to.
nc -h 2>&1 | head -2; dpkg-query -W -f='${Package} ${Version}\n' netcat-openbsd
That first line is the tell. A netcat-openbsd build prints OpenBSD netcat with a Debian patchlevel, netcat-traditional prints its own version banner, and ncat prints a usage block that names Nmap.

Install by package name rather than by the netcat metapackage, because that metapackage resolves to whichever variant your distribution prefers.
sudo apt install netcat-openbsd # Debian and Ubuntu
sudo dnf install nmap-ncat # Fedora and RHEL
sudo pacman -S openbsd-netcat # Arch
| Variant | Package name | Ships as nc on | Flags the others lack |
|---|---|---|---|
| netcat-openbsd | netcat-openbsd | Debian, Ubuntu, Alpine | -q, -N, -I, -O |
| netcat-traditional | netcat-traditional | older Debian, EPEL | -e, -c, -o, -G |
| ncat | nmap-ncat | Fedora, RHEL, CentOS, Windows | –ssl, –sh-exec, –proxy, –allow |
Connect mode and listen mode
Every nc command is one of two shapes, either a connect to a host and port or a listen on a port that waits for someone to arrive.
nc [options] host port # connect mode
nc -l port # listen mode
A hostname that will not resolve fails before netcat opens anything, so check name resolution first when a connect dies immediately. The exact error text differs by variant, and Ubuntu prints a name or service not known message that points at how your interface address and DNS resolver are configured rather than at the port.
Running both ends on one machine is the fastest way to watch the pair work, with the listener waiting on port 9002 and writing whatever arrives into a file while the client sends a single line.
nc -l 9002 > /tmp/nc-recv.txt & sleep 0.5; printf 'ping from the client\n' | nc -q 1 127.0.0.1 9002; wait; cat /tmp/nc-recv.txt

Without -q 1 the client keeps the connection open after its input ends, so the command appears to hang even though the listener already received the line.
OpenBSD nc accepts the listen port positionally or through -p, so nc -l 3000 and nc -l -p 3000 reach the same place. In connect mode -p means something else entirely, and it sets the local source port rather than a destination.
Two-way chat between two terminals
A netcat connection is a stream in both directions, so two terminals on the same machine turn it into a chat channel where neither end formats anything.


The listener accepts a single connection and prints every line the other side types, and the client writes into the same stream. Closing either end ends the conversation.
Check whether a port is open
Scanning uses zero-I/O mode, which opens a connection and closes it without sending a byte. Add verbose mode to see the failures as well as the successes.
nc -zv 127.0.0.1 9003 2>&1; echo "exit status: $?"; nc -l 9004 & sleep 0.5; nc -zv 127.0.0.1 9004 2>&1; echo "exit status: $?"

The exit status is what a script can act on. A refused port returns 1 and an open port returns 0, so you can test a service without parsing any text.
| What you want | Command | What comes back |
|---|---|---|
| one port | nc -zv host port | succeeded line and exit 0, or refused and exit 1 |
| a range | nc -zv host 8000-8010 | one line per port, successes and refusals alike |
| several ports | nc -z host 22 80 443 | only the ports that answered |
| several ports, quiet | nc -z host 22 80 443 | nothing at all when every port is closed |
A range scan is the same command with the ports joined by a hyphen, so every port in the range gets its own line. The refused entries are what tell you the range actually ran.

Pointing the scan at a remote host proves that the service behind the port accepted the connection, which is a different question from whether the host answers at all.

A refused connection is not proof that a service is down. A firewall that drops the packet instead of rejecting it makes netcat wait for the timeout, which is why a scan that hangs is a firewall and SELinux question before it is a netcat question. A listener bound to 127.0.0.1 also refuses a scan sent to the machine’s public address.
Read what a service sends back
An open port only tells you something is listening. Send a request and read the answer to find out what it is.
printf 'GET / HTTP/1.0\r\nHost: example.com\r\n\r\n' | nc -w 6 example.com 80 | head -8

The status line and the Server header come back exactly as the service writes them, which is how you confirm that a proxy or CDN sits in front of a host. The newline characters are part of the protocol, and a request that ends without its blank line gets no reply at all.
A service that greets clients first needs no request. OpenSSH writes its version string the moment the connection opens, so a bare connect is enough to identify it.
nc -w 4 127.0.0.1 22

When you want the body of a page rather than its headers, an HTTP client is shorter and handles redirects for you, and downloading a file with curl covers the request flags that netcat cannot tell you about.
Move a file between two machines
Netcat copies a file by piping bytes into a connection on one side and out of a listener on the other. There is no encryption and no checksum, so this suits a trusted network and nothing else.
printf 'netcat file transfer payload\n' > /tmp/nc-payload.txt; nc -l 9005 > /tmp/nc-received.txt & sleep 0.5; nc -q 2 127.0.0.1 9005 < /tmp/nc-payload.txt; wait; diff /tmp/nc-payload.txt /tmp/nc-received.txt && echo 'files are identical'

The diff prints nothing and the confirmation line prints, which means the bytes arrived unchanged. Across machines the order matters, because the receiver has to be listening before the sender connects.
- Start the receiver and give it a file to write into. nc -l 9005 > received.txt
- Start the sender and feed it the file. nc -q 2 192.168.1.20 9005 < payload.txt
- Compare the payload with the copy that arrived. diff payload.txt received.txt
Anything crossing an untrusted path should use an encrypted transfer instead, and a plain download from a URL does not need a listener at all. Setting up an SFTP client keeps the same shape as the commands above, and downloading a file with wget is the shorter route when the file already lives on a web server.
Keep a listener alive for repeated connections
A listener without -k accepts one connection and exits, which is the reason a second client gets a refusal on a port that was working a minute ago.
nc -lk 9006 > /tmp/nc-keepopen.txt & L=$!; sleep 0.5; printf 'first connection\n' | nc -q 1 127.0.0.1 9006; sleep 1; printf 'second connection\n' | nc -q 1 127.0.0.1 9006; sleep 0.5; kill $L; cat /tmp/nc-keepopen.txt

Both lines land in the same file because the listener stayed up between them. The -k flag is what turns nc into a small service you can test against, so the process now keeps running until you stop it.
When a client cannot connect, look at the socket before you blame the client. A listener that already exited and a listener that never started produce the same refusal, and only the socket table tells them apart, so reading listening sockets with ss and netstat is the check to run next. It is also the fastest way to confirm that a container published its port to the host.
Send UDP datagrams instead of TCP
Add -u on both ends to move from a byte stream to datagrams. A UDP listener has nothing to accept, because no connection is ever established.
nc -lu 9007 > /tmp/nc-udp.txt & L=$!; sleep 0.5; printf 'udp datagram\n' | nc -u -q 1 127.0.0.1 9007; sleep 0.3; kill $L; cat /tmp/nc-udp.txt

Nothing confirms delivery, because the receiving end never acknowledges a datagram. If the line does not appear, the packet either went to the wrong port or was dropped on the way.
- The flag is needed on the sender and on the listener, and dropping it on either end gives you a silent mismatch between TCP and UDP.
- -k with -u keeps one socket open for datagrams from more than one host.
- A closed UDP port sends no refusal message, so netcat reports nothing rather than an error.
Options that were removed
The backdoor one-liner that appears in older netcat guides does not run on the nc that ships with Ubuntu or Debian.
nc -w 3 -e /bin/sh 127.0.0.1 9001 2>&1

OpenBSD nc has no -e option at all, so the shell redirect that those guides rely on is gone from the default binary. The same holds for a handful of other traditional flags, and the usage block netcat prints when it rejects an option is the authoritative list for your build.
| Traditional flag | What it did | Where it still exists |
|---|---|---|
| -e program | redirect the socket to a program | ncat –sh-exec |
| -c commands | run a shell command after connecting | ncat –sh-exec |
| -o file | write a hex dump of the traffic | tcpdump or socat |
| -g and -G | source-route packets over IPv4 | removed everywhere |
The behavior behind -e is a genuine security problem, and removing it from the default binary was deliberate. If you need to attach a program to a socket, install ncat and read its own option list rather than assuming the flag survived.
Frequently asked questions
The answers below settle which command to reach for, and each one matches the nc on a current Debian or Ubuntu system.
What is the netcat command in Linux?
Netcat is a command-line tool that reads and writes raw data over TCP, UDP, and UNIX-domain sockets. The name nc usually points to netcat-openbsd on Debian and Ubuntu, and to ncat on Fedora and RHEL.
How do I check whether a port is open with netcat?
Run nc -zv host port and read the exit status. A status of 0 means something accepted the connection, and a status of 1 means the port refused it or never answered.
Why does my nc client hang after sending a line?
The client waits for the connection to close after its input ends. Add -q 1 on the sender, or -N to shut the socket down as soon as standard input reaches end of file.
Why does nc -l -p give an error on my system?
Listen mode takes the port as a positional argument, and netcat-traditional is the variant that requires -p. If you see an error, run nc -h and match your command to the option list that build actually prints.
Can netcat transfer files securely?
No. Netcat sends the file as plain bytes with no encryption and no integrity check. Use scp or sftp whenever the transfer crosses a network you do not control.
