Skip to content
ansezz.

▸ Free tool

Common Ports.

The TCP and UDP ports you actually grep for at 2am, grouped by what runs on them, with a note on each one that matters. Type in the box to filter the whole reference.

▸ Static page

Every table here is server-rendered HTML. The only script on the page is the filter box, and it never sends anything anywhere.

Web, proxies & HTTP

Two of these carry almost all of your traffic, and the rest exist because 80 and 443 are privileged. Anything that cannot bind below 1024 — a JVM, a Node process, a container running as a non-root user — picks 8080 or 8443 and lets a proxy or a port mapping do the translation. That is the entire reason those numbers are so crowded.

The one that catches people is 443 being both TCP and UDP. HTTP/3 runs over QUIC on UDP/443, and a firewall written by somebody who has only ever thought about TCP will let the site work perfectly while quietly forcing every client back down to HTTP/2.

Web, proxy and HTTP ports
Port Proto Service Note
80 TCP HTTP Plaintext. Its only honest job now is a 301 to 443 — an API that still serves real responses here is handing bearer tokens to whoever owns the café Wi-Fi.
443 TCP/UDP HTTPS · HTTP/3 (QUIC) TLS over TCP, and QUIC over UDP for HTTP/3. When HTTP/3 silently never negotiates, it is almost always UDP/443 dropped by a firewall someone only ever wrote a TCP rule for.
8080 TCP HTTP alternate Tomcat, Spring Boot, Jenkins, cAdvisor, Weaviate and the Traefik dashboard all want it. The most over-subscribed number on any developer laptop.
8443 TCP HTTPS alternate The TLS twin of 8080. Unprivileged, so a JVM or an ingress controller can bind it without root — which is exactly why it exists.
3128 TCP Squid forward proxy The classic corporate egress proxy. If a container resolves DNS fine but every outbound request hangs, check whether HTTPS_PROXY should be pointing here.
1080 TCP SOCKS proxy SOCKS5. ssh -D 1080 user@bastion gives you one for free, and it is the cheapest way to reach a private subnet from a laptop without provisioning a VPN.
2019 Local only TCP Caddy admin API Loopback by default, and it must stay there: anyone who reaches it can rewrite the entire config and get certificates issued for any hostname you control.

Databases & search

Nothing in this table belongs on a public address. Databases are the most reliably profitable thing to find open, so they are scanned constantly — and the default configuration of most of them assumes it is sitting on a trusted network, because in 1998 it was.

Two habits make the whole table safe: bind to a private interface or loopback and let the app reach it over the VPC, and reach it yourself through an SSH tunnel rather than a firewall exception you meant to remove. ssh -L 5432:127.0.0.1:5432 bastion takes three seconds and expires when you close the terminal.

Database and search engine ports
Port Proto Service Note
3306 Never public TCP MySQL · MariaDB The classic protocol. Exposed to the internet it gets credential-stuffed continuously — bind it to a private address and let the app reach it inside the VPC.
33060 Never public TCP MySQL X Protocol MySQL 8's document protocol, enabled by default. People firewall 3306, forget this exists, and leave the same server reachable through the side door.
5432 Never public TCP PostgreSQL listen_addresses and pg_hba.conf are two independent gates. Opening the port without a matching pg_hba line only changes which error you get.
6432 TCP PgBouncer The pooler that fronts 5432. Past a few hundred clients your app should talk to this instead — and remember transaction mode breaks session state and server-side prepared statements.
1433 Never public TCP Microsoft SQL Server The default instance. Named instances get dynamic ports and are found through 1434/UDP, which is why so many SQL Server firewall rules are subtly wrong.
1434 UDP SQL Server Browser Instance discovery, and a historic amplification vector. Pin your named instances to static ports and you can switch it off entirely.
1521 Never public TCP Oracle TNS listener The listener, not the database. It can redirect a client to another port entirely, so one firewall rule for 1521 is frequently not enough.
27017 Never public TCP MongoDB Binds localhost by default since 3.6 — the ransom-note era came from builds that did not. Verify with ss rather than trusting whatever your distro packaged.
27018 · 27019 TCP MongoDB shard · config server Shard members and config servers in a sharded cluster. Cluster-internal traffic only; nothing outside should ever dial these.
9042 TCP Cassandra · ScyllaDB (CQL) The client protocol. Inter-node gossip runs on 7000, or 7001 with TLS, and it has to be open between nodes or the ring never forms.
8123 · 9000 TCP ClickHouse HTTP · native 9000 is the fast native protocol, and it collides with php-fpm, MinIO and Portainer. On a shared box, somebody is moving.
9200 · 9300 Never public TCP Elasticsearch · OpenSearch 9200 is the REST API, 9300 the inter-node transport. An open, unauthenticated 9200 is still one of the most-indexed data leaks on Shodan.
7687 · 7474 TCP Neo4j Bolt · HTTP Bolt is the binary driver protocol, 7474 the browser UI. Change the default password before that container lives anywhere but your laptop.
6333 · 6334 TCP Qdrant REST · gRPC Vector store. The API key is opt-in, so an unset key means anyone who reaches 6333 can read — and quietly rewrite — your embeddings.
19530 TCP Milvus The gRPC endpoint for the vector database; 9091 serves metrics and health, which collides with the Prometheus Pushgateway.

Caches, queues & streaming

The pattern to notice here is that clustered systems need more ports than the one you configured. Redis Cluster needs its data port plus 10000. RabbitMQ needs epmd on 4369 and the Erlang distribution port alongside AMQP. Kafka needs whatever its listeners advertise, which is rarely what you typed. Half your first-cluster problems are one missing firewall rule between nodes.

The second pattern: the management interface is usually the softest part. 15672, 8222 and a Kafka UI container are all far easier to get into than the broker protocol itself, and they all hand over enough to drain a queue.

Cache, queue and streaming ports
Port Proto Service Note
6379 Never public TCP Redis · Valkey The most abused open port on the internet. An unauthenticated instance is a write primitive: CONFIG SET dir, SAVE, and your authorized_keys file belongs to someone else.
26379 TCP Redis Sentinel Failover coordination. Clients ask Sentinel who the master is, so when this is unreachable your app fails over to nothing at all.
16379 TCP Redis cluster bus Always the data port plus 10000. Open 6379 between nodes but not 16379 and the cluster never finishes forming — the classic first-cluster bug.
11211 Never public TCP/UDP Memcached No authentication in the default build. Its UDP listener drove the 1.3 Tbps amplification attacks of 2018 and now ships disabled — check anything older than that.
5672 · 5671 TCP RabbitMQ AMQP · AMQPS 5671 is the TLS listener. Publisher confirms and consumer acks are what make delivery reliable — an open port only proves the socket works.
15672 Never public TCP RabbitMQ management UI guest/guest is restricted to loopback, but a reverse proxy on the same host makes every request look like loopback. That is how an exposed dashboard becomes an open one.
15692 TCP RabbitMQ Prometheus metrics From the rabbitmq_prometheus plugin. Scrape this instead of polling the management HTTP API, which gets expensive on a vhost with thousands of queues.
25672 · 4369 Never public TCP Erlang distribution · epmd Cluster traffic and the Erlang port mapper. A reachable epmd plus a guessable cookie is remote code execution on the node — keep both cluster-internal.
9092 TCP Kafka broker The advertised.listeners value matters more than the port number: clients are told where to reconnect, and inside Docker that answer is usually wrong.
9093 TCP Kafka TLS · KRaft controller Kafka's second listener by convention, and the KRaft controller quorum port. Also Alertmanager's default — do not put both on one host without thinking about it.
2181 TCP ZooKeeper Client port for pre-KRaft Kafka, HBase and friends. Peer traffic is 2888 and leader election 3888, and all three need to be open between ensemble members.
4222 · 6222 · 8222 TCP NATS client · route · monitoring 8222 is an unauthenticated monitoring endpoint listing connections and subjects. Useful internally, embarrassing publicly.
1883 · 8883 TCP MQTT · MQTT over TLS 1883 is plaintext. Mosquitto allowed anonymous publish by default for years, which is how strangers ended up toggling other people's devices.

Containers & orchestration

This is the highest-consequence table on the page. 2375 is root on the host. 6443 is every workload in the cluster. 10250 is exec into any pod on the node. 2379 is every Secret you have. None of these has a safe failure mode, and all of them are in the standard scan list.

The other trap is the overlay network. Container-to-container traffic between hosts rides UDP — 4789 for VXLAN, 8472 for flannel and k3s — and a security group that only allows TCP produces the worst kind of outage: everything reports healthy, and nothing can talk to anything on another node.

Container and orchestration ports
Port Proto Service Note
2375 Never public TCP Docker daemon (plaintext) Unauthenticated root on the host, dressed up as an HTTP API. One request starts a container with the host filesystem mounted. Bots find a fresh one within minutes.
2376 TCP Docker daemon (TLS) The same API with mutual TLS. Correct if you genuinely need remote access — but DOCKER_HOST=ssh://host is simpler and has one fewer certificate to expire.
2377 TCP Swarm cluster management The manager-to-manager control plane. Open it between managers only, never to workers and never to the internet.
7946 TCP/UDP Swarm node gossip Both protocols, both directions. Half-open here is the classic cause of a Swarm service that reports as running but never receives a single request.
4789 UDP VXLAN overlay data plane The tunnel carrying container-to-container traffic between hosts. Cloud firewalls written for TCP only will break every overlay network silently.
6443 TCP Kubernetes API server The entire cluster behind one port. kubeadm and k3s both default here; some managed distributions front it on 443 instead.
10250 Never public TCP kubelet API Logs and exec for every pod on that node. Left with anonymous auth enabled it has been a full cluster takeover more than once — control plane and node-internal only.
10256 · 10257 · 10259 TCP kube-proxy · controller-manager · scheduler Health and metrics endpoints for the control plane components. Worth scraping from inside the cluster, never worth exposing outside it.
2379 · 2380 Never public TCP etcd client · peer etcd holds every Secret in the cluster, base64-encoded rather than encrypted unless you enabled encryption at rest. Read access here is total compromise.
30000–32767 TCP/UDP Kubernetes NodePort range Every NodePort service opens its port on every node. Fine for a demo, a liability in a cluster with public node IPs — use an ingress or a LoadBalancer.
8472 UDP Flannel VXLAN Flannel's tunnel port and the k3s default. Same failure mode as 4789: pod-to-pod traffic across nodes disappears without a single error message.
5000 TCP Docker registry (distribution) Serves plain HTTP out of the box, so clients refuse it until you add TLS or an insecure-registries entry. On macOS it also fights AirPlay Receiver.

Observability

Almost everything in this stack ships unauthenticated, because the assumption is that it lives on a private network behind a proxy that handles identity. That assumption is load-bearing: an open Prometheus hands over your internal hostnames, your service topology and your traffic patterns, and an open Loki hands over whatever your application logs — which, if anyone was careless, includes tokens.

The numbers also collide more than anywhere else. 3000 is Grafana and every JavaScript dev server. 9093 is Alertmanager and Kafka's TLS listener. 9091 is the Pushgateway and Milvus metrics. Read the process name from ss -tulpn, not the port number.

Metrics, logging and tracing ports
Port Proto Service Note
9090 Never public TCP Prometheus UI and query API on one port, unauthenticated by design — it expects a proxy in front. Your metrics are an org chart and a topology map for anyone who reads them.
9091 TCP Pushgateway For batch jobs that die before a scrape can happen. Every other use is a mistake: it never expires series and becomes a graveyard of stale metrics.
9093 TCP Alertmanager Also Kafka's usual TLS listener, so read the process name before you conclude anything. Peering between Alertmanager replicas is 9094, TCP and UDP.
9100 TCP node_exporter Host metrics. The informal exporter range starts here and climbs: 9104 for MySQL, 9121 for Redis, 9187 for Postgres.
3000 TCP Grafana Shares its number with roughly every JavaScript dev server ever written. On a box that also runs a Node app, one of the two is moving to 3001.
3100 TCP Loki Push and query API for logs. Multi-tenancy is a header, not a port, so an open 3100 reads every tenant you have.
3200 TCP Tempo The query API for traces. Ingest is separate and usually OTLP on 4317 or 4318 — that is the half people forget to open.
4317 · 4318 TCP OTLP gRPC · HTTP The OpenTelemetry collector's two receivers. 4317 is gRPC, 4318 is HTTP/protobuf; sending one to the other gives you a socket that opens and then says nothing useful.
16686 TCP Jaeger UI The trace query UI. Modern deployments ingest over OTLP on 4317/4318 rather than Jaeger's own legacy collector ports.
9411 TCP Zipkin Still the fallback exporter in plenty of Spring and Micrometer stacks. Most collectors accept the Zipkin format here purely for compatibility.
8125 UDP StatsD Fire and forget. Lost packets are invisible, which is precisely the trade you accepted in exchange for never blocking the request path.
24224 TCP/UDP Fluentd · Fluent Bit forward The forward protocol between log agents and aggregators. Docker's fluentd log driver targets it — and blocks the container when the aggregator is down.
5601 TCP Kibana · OpenSearch Dashboards The window onto 9200. Locking down Kibana while leaving Elasticsearch itself open is the ELK equivalent of bolting the door and opening the window.
514 Legacy UDP syslog Unauthenticated, unencrypted and lossy — anyone who can reach it can forge log lines. Use 6514 for syslog over TLS when the data has to be trustworthy.

Mail

Mail has two jobs on two different sets of ports, and mixing them up is why so much application mail disappears. Relay is server-to-server on 25, and every major cloud blocks it outbound by default. Submission is your app authenticating to a provider, and that is 587 with STARTTLS or 465 with implicit TLS.

If mail works on your laptop and silently fails on the VM, you are almost certainly submitting on 25 and being dropped by the provider's egress policy. Switch to 587 or 465, or 2525 if both are filtered — and never treat a successful TCP connect as proof that a message was accepted.

Mail transport and retrieval ports
Port Proto Service Note
25 TCP SMTP relay (MTA to MTA) Server-to-server delivery. Every major cloud blocks outbound 25 by default to fight spam, which is why your app's mail silently vanished on a fresh VM.
587 TCP SMTP submission (STARTTLS) Where an application should submit authenticated mail. STARTTLS starts in plaintext, so the client has to require the upgrade rather than merely accept it.
465 TCP SMTPS (implicit TLS) Deprecated for a decade, then formally recommended again by RFC 8314. TLS from the first byte with nothing to downgrade — prefer it when the provider offers both.
2525 TCP SMTP submission (alternate) Not a standard, just a convention. SendGrid, Mailgun and Postmark listen here because so many networks block 25 and some block 587 too.
143 · 993 TCP IMAP · IMAPS 993 is implicit TLS and the only one worth enabling. 143 with STARTTLS exists mostly for clients that stopped being updated in 2009.
110 · 995 Legacy TCP POP3 · POP3S POP3 downloads and forgets. If something still needs it, give it 995 — plaintext 110 hands credentials to the network on every poll.
1025 · 8025 Local only TCP Mailpit · MailHog SMTP + UI Local mail traps for development: point the app at 1025 and read what it tried to send at 8025. Nothing leaves the machine, which is the point.

Remote access & file transfer

One of these is fine on a public address if you configure it properly, and it is 22. The rest — RDP, VNC, SMB, Telnet, FTP — belong behind a VPN, a bastion, or an identity-aware proxy. RDP in particular is the most common initial access vector in ransomware incident reports, year after year.

SSH is also the tool that makes everything else on this page unnecessary. ssh -L 5432:127.0.0.1:5432 bastion gives you a database on localhost with no firewall change, no exception to forget about, and an audit trail. Reach for that before you open a port.

Remote access and file transfer ports
Port Proto Service Note
22 TCP SSH · SFTP · SCP Moving it to 2222 reduces log noise, not risk. Keys only, PasswordAuthentication no, and put it behind a bastion or a VPN if the host is anywhere public.
3389 Never public TCP/UDP RDP The most common ransomware entry point there is. This is not a rule to be casual about: gateway or VPN, never a raw public listener.
5900 Never public TCP VNC 5900 plus the display number, so :1 is 5901. The protocol's own password is capped at eight characters — tunnel it over SSH instead of trusting it.
445 Never public TCP SMB File sharing, and the port EternalBlue and WannaCry rode across the world. It should never cross a network boundary; consumer ISPs block it for good reason.
139 · 137/138 Legacy TCP/UDP NetBIOS session · name service Pre-SMB2 legacy. Still enabled across plenty of Windows estates, still handing hostnames and share lists to anyone on the LAN.
111 Never public TCP/UDP rpcbind · portmapper The directory that tells clients which random ports NFS's helper daemons landed on. Also a UDP amplification source, which makes it a scanner favourite.
2049 TCP NFS NFSv4 needs this port alone. v3 also needs 111 plus mountd, statd and lockd on whatever ports they happened to pick, which is why v3 firewall rules are miserable.
21 · 20 Legacy TCP FTP control · data Credentials in the clear, plus a second connection on a port negotiated at runtime — the reason FTP and NAT never got along. Use SFTP over 22.
990 Legacy TCP FTPS (implicit TLS) FTP wrapped in TLS. It still drags the two-connection design along with it; SFTP avoids the entire problem for free.
23 Legacy TCP Telnet A plaintext shell. If something on your network still answers here it is a switch, a printer, or an IoT device that belongs on its own VLAN.
5985 · 5986 TCP WinRM HTTP · HTTPS How Ansible and PowerShell Remoting drive Windows hosts. 5985 is called HTTP but the payload is usually encrypted at the protocol layer — which is not the same as TLS.

DNS, discovery & network services

These are the services that make everything else findable, and they are almost all UDP-first, which changes how they fail. A dropped UDP packet produces a timeout and a retry, not an error — so a broken DNS or NTP path shows up as “the app is slow sometimes” rather than anything that looks like a network fault.

They are also the classic amplification sources: 53, 123, 161, 111 and 11211 all answer small questions with large responses, which is exactly what a reflection attack needs. If you run any of them on a public address, rate-limit them and disable the verbose queries.

DNS, discovery and network service ports
Port Proto Service Note
53 TCP/UDP DNS UDP for most answers, TCP for anything over 512 bytes and for zone transfers. Blocking TCP/53 because “DNS is UDP” breaks DNSSEC and large answers in ways that look like random flakiness.
853 TCP DNS over TLS (DoT) Encrypted DNS on a port of its own, so a network operator can see it and decide. DNS over HTTPS hides inside 443 instead, which is the entire argument about it.
5353 UDP mDNS · Bonjour How .local names resolve with no server involved. Chatty, LAN-scoped, and the reason a discovery trick that works at the office dies in the VPC.
123 UDP NTP Clock sync — and clock skew is what quietly breaks TLS handshakes, JWT validation and TOTP codes. Historic amplification vector; modern configs disable the query that caused it.
161 · 162 UDP SNMP · SNMP traps v1 and v2c authenticate with a plaintext community string, and “public” is still live on real hardware today. Use v3, or keep it on a management VLAN.
389 · 636 TCP LDAP · LDAPS 389 can be upgraded with StartTLS, 636 is TLS from the start. Active Directory adds the global catalog on 3268 and 3269, which is what most integrations actually query.
88 TCP/UDP Kerberos Ticket granting. It is also why clock skew beyond five minutes locks an entire domain out of everything — see NTP above.
67 · 68 UDP DHCP server · client Broadcast, so it does not cross a router without a relay. A rogue DHCP server on a flat network is a trivial man-in-the-middle.
8500 · 8600 TCP/UDP Consul HTTP API · DNS 8600 answers DNS for .consul names and 8301 carries the LAN gossip. An 8500 with ACLs left disabled is read-write access to your service catalog.
8200 · 8201 TCP Vault API · cluster 8200 is the API and UI, 8201 replication between nodes. The unseal keys are the real perimeter here — the port is just where the conversation happens.
5060 · 5061 Never public TCP/UDP SIP · SIP over TLS An open 5060 gets brute-forced for toll fraud within hours. Media does not ride SIP either — RTP takes its own UDP range, typically 10000–20000.
51820 UDP WireGuard It never answers an unauthenticated packet, so a port scan finds nothing at all. The endpoint is effectively invisible unless you hold a key.
1194 UDP OpenVPN UDP by default. The TCP-on-443 fallback exists to get through hostile networks and pays for it with TCP-over-TCP meltdown the moment there is packet loss.

Dev servers & local ports

None of these are standardised — they are conventions that stuck, and they collide constantly. 3000 is Next.js and Grafana. 5000 is Flask and a Docker registry and, on any recent Mac, AirPlay Receiver. 9000 is php-fpm and MinIO and Portainer and ClickHouse's native protocol. When a framework tells you the port is in use, it is usually telling the truth about somebody else's process.

The rule that keeps this table harmless: dev servers bind 127.0.0.1 and stay there. The moment one binds 0.0.0.0 on a café network — a Jupyter notebook, an Ollama instance, a Node process with the inspector attached — it is an unauthenticated remote shell with a friendly UI.

Development server ports
Port Proto Service Note
3000 Local only TCP Node.js dev servers Next.js, Express, NestJS, Rails, Create React App — and Grafana. Next increments to 3001 when 3000 is busy, which is why your OAuth callback URL suddenly stopped matching.
3001 Local only TCP Second app or API Where the API goes once the front end has claimed 3000. Nothing standard about it — it is just the number everyone reaches for second.
4321 Local only TCP Astro dev server Counts down 4-3-2-1, which is the whole joke. astro preview uses it too, so with a dev server already running preview lands on 4322.
5173 Local only TCP Vite Vite and everything built on it: SvelteKit, Nuxt, modern React Router. Takes 5174 next, and HMR shares the port unless you split it explicitly.
4200 Local only TCP Angular CLI ng serve, unchanged since the AngularJS-era tooling. One of the few dev ports you can still guess correctly on the first try.
8000 Local only TCP Python · PHP dev servers Django runserver, uvicorn, FastAPI, php -S, python -m http.server, Chroma. Wildly overloaded — check what is already bound before blaming your framework.
8888 Never public TCP Jupyter The token in the startup URL is the only authentication there is. Publishing this port without setting a password is publishing a Python shell.
9000 Never public TCP php-fpm (FastCGI) Not HTTP — a browser aimed at it gets nothing, because only a FastCGI client speaks the protocol. Prefer the Unix socket; an exposed 9000 is remote code execution.
9229 Never public TCP Node.js inspector --inspect binds loopback deliberately: the debug protocol has no auth and evaluates arbitrary code. --inspect=0.0.0.0 in a published container is a backdoor.
6006 Local only TCP Storybook · TensorBoard Storybook's default, and TensorBoard's as well. Both are viewers people start once and leave running for a fortnight.
5000 Local only TCP Flask · macOS AirPlay Receiver Flask's default, and since Monterey macOS claims 5000 and 7000 for AirPlay. The mysterious “address already in use” with nothing in ps is System Settings, not you.
11434 Local only TCP Ollama The local model API. It answers with no authentication, so setting OLLAMA_HOST to 0.0.0.0 on a shared network lends your GPU to the entire office.
7860 · 8501 Local only TCP Gradio · Streamlit The two default demo-app ports of the ML world. Gradio's share links tunnel through a public relay — understand what that means before using one at work.

The three ranges, and why 80 needs root

A port is 16 bits, so there are 65,535 usable ones per protocol per address. IANA splits them into three ranges, and only the first one is enforced by anything.

  • 0–1023, well-known. Assigned by IANA, and privileged on every Unix: binding one requires root or the CAP_NET_BIND_SERVICE capability. The rule dates to multi-user machines, where the point was stopping an ordinary user from standing up a fake telnet or SMTP daemon on the port everyone trusted.
  • 1024–49151, registered. IANA keeps a list, but registration is a filing, not a reservation. Nothing stops you binding 3306 with something that is not MySQL, and plenty of software squats on numbers it never registered. Treat every entry here as a strong convention rather than a guarantee.
  • 49152–65535, dynamic and private. Officially the ephemeral range — the pool the kernel picks from for the source port of an outbound connection. In practice Linux ignores IANA and defaults to 32768–60999 (net.ipv4.ip_local_port_range); Windows uses the IANA range.

The privileged-port rule has three normal escapes and you should pick one deliberately. In systemd, give the unit AmbientCapabilities=CAP_NET_BIND_SERVICE and keep running as a normal user. On Linux, lower net.ipv4.ip_unprivileged_port_start if you would rather change the policy than the process. Or do what almost everyone does: bind 8080 and let nginx, a load balancer, or a container port mapping own 80 and 443. Inside a container the question mostly disappears — the process is root in its own namespace and gets NET_BIND_SERVICE by default — but rootless Docker and Podman run into the host's rule again when they try to publish a port under 1024.

Two more facts worth having. A listening socket is unique per protocol, address and port, not per port alone: TCP/53 and UDP/53 are different sockets, and two processes can both bind 8080 on different addresses. And ephemeral exhaustion is real — a busy NAT gateway or forward proxy runs out of source ports and starts failing with “cannot assign requested address”, which reads like a bug in your code and is not.

The ports that get people owned

There is no grace period on a public IPv4 address. Mass scanners sweep the entire address space in minutes, and a handful of ports are profitable enough that they are checked first, every time. These are the ones that turn a misconfiguration into an incident.

▸ Never on a public address

  • 2375 — Docker daemon. No authentication at all. A single request starts a privileged container with the host filesystem mounted, which is root. This is the fastest full compromise on the list.
  • 6379 — Redis. Before Redis 6 there was no user model, and protected mode only helps when you have not overridden the bind address. The classic exploit does not even need a bug: point the dump file at an SSH authorized_keys file, write a key as a value, SAVE, log in.
  • 9200 — Elasticsearch. An unauthenticated REST API over the whole index. This is where the “hacker leaves a ransom note in your database” stories come from.
  • 27017 — MongoDB. Same story, same decade. Newer builds bind loopback by default, which is why the incidents thinned out rather than stopped.
  • 10250 and 2379 — kubelet and etcd. Exec into any pod, or read every Secret in the cluster. Both have been the root cause of published breaches.
  • 3389 — RDP. The single most common ransomware entry point. Credential stuffing does not need an exploit.
  • 5060 — SIP. Brute-forced within hours for toll fraud, which bills in real money while you sleep.

The Docker publish trap nobody warns you about

Publishing a port with -p 6379:6379 binds 0.0.0.0, not localhost. Worse, Docker writes its own iptables rules into the DOCKER chain, and those are evaluated before the rules UFW manages — so ufw deny 6379 can be sitting there looking correct while the container is happily reachable from the internet. People discover this after the fact, in logs.

The fix is one prefix: -p 127.0.0.1:6379:6379 publishes to loopback only. For anything that genuinely needs to be reachable from another host, put the rule in the DOCKER-USER chain, which is evaluated first and is the officially supported hook. And if you only need it from your laptop, do not publish it at all — docker exec or an SSH tunnel gets you there without opening anything.

What to do instead

Bind to 127.0.0.1 or a private interface by default, and make the exception the thing you have to justify. Reach internal services through an SSH tunnel or a VPN rather than a firewall rule you will forget to remove. If something truly has to be public, put an authenticating proxy in front of it — not a password field bolted onto an admin UI that was never designed to face the internet. Then verify from outside: run a scan against your own public address and read the result, because your firewall config is a description of intent and the scan is the truth.

Finding what is actually listening

Every one of these answers a slightly different question, and the flags matter more than the command. -t is TCP, -u is UDP, -l is listening sockets only, -p shows the owning process, and -n skips DNS resolution so the output appears instantly instead of after a timeout. Without sudo you will see the sockets but not the process names of other users.

listeners
# Linux, the modern one.  -t TCP  -u UDP  -l listening  -p process  -n numeric
ss -tulpn
ss -tulpn | grep ':5432'

# Only the sockets reachable from outside this box
ss -tulpn | grep -v '127.0.0.1' | grep -v '\[::1\]'

# macOS (and Linux) — who is holding 8080
lsof -nP -iTCP:8080 -sTCP:LISTEN

# Older hosts, or when ss is missing. Deprecated on most distros.
netstat -tulpn

# Evict whatever is squatting on 3000
fuser -k 3000/tcp

Read the address column, not just the port. 127.0.0.1:5432 is loopback only. 0.0.0.0:5432 is every IPv4 interface including the public one. [::]:5432 is every IPv6 interface — and on most Linux systems that covers IPv4 too, via mapped addresses, which is a detail that has surprised a lot of people who thought they were bound to v6 only.

containers-and-windows
# What a container actually published, and on which interface
docker port my-api
docker ps --format 'table {{.Names}}\t{{.Ports}}'

# 0.0.0.0:6379->6379/tcp   the whole internet
# 127.0.0.1:6379->6379/tcp loopback only  <- what you almost always want

# The app binds inside the container, so check there too
docker exec my-api ss -tulpn

# Kubernetes: what the service claims vs what the pod listens on
kubectl get svc -A
kubectl exec deploy/api -- ss -tulpn

# Windows
netstat -ano | findstr :3000
Get-NetTCPConnection -LocalPort 3000 -State Listen

The container case has its own classic failure: the port mapping is correct, the container is running, and connections still fail — because the process inside bound 127.0.0.1 instead of 0.0.0.0. Inside a container, loopback means the container's own loopback, so the mapping has nothing to forward to. Frameworks that default to localhost in development (Flask, Rails, plenty of Node setups) need an explicit host flag once containerised.

reachability
# Can I reach it at all? Fast, no payload sent.
nc -vz db.internal 5432

# UDP: silence proves nothing. A closed port answers with ICMP
# that the firewall in between probably swallowed.
nc -vzu ntp.internal 123

# Refused vs timed out, with a timeout you control
curl -v --connect-timeout 3 telnet://db.internal:5432

# Where does the packet actually die
sudo traceroute -T -p 443 api.example.com

Refused vs timed out — the free diagnosis

Two failures that look identical in an application log tell you completely different things, and the difference is free. It is the single most useful five seconds in network debugging: one says the packets are arriving and the service is not there, the other says the packets are not arriving at all.

  Connection refused Connection timed out
What came back A TCP RST. Something answered you. Nothing at all. The SYN was swallowed.
How long it took Instant — one round trip. Seconds, until your connect timeout fires.
What it proves Routing, DNS and the firewall path are fine. No process is listening on that port, or a rule is REJECTing it. Packets are being dropped: security group, DROP rule, wrong address, or no route to the host.
Check first Is the service up? ss -tulpn. Is it bound to 127.0.0.1 rather than 0.0.0.0? Security group and firewall rules, then the path: sudo traceroute -T -p PORT host.
Classic cause Container publishes the port; the app inside binds loopback. AWS security group missing the inbound rule, or you are on the wrong VPN.

There are two more states worth naming. Connects, then hangs means the TCP handshake worked and the protocols disagree — you spoke HTTP to php-fpm on 9000, or plaintext to a TLS-only listener, and both sides are now waiting for the other to say something. Connection reset by peer mid-stream means it worked and then stopped: a proxy timeout, a TLS version the server rejected after the handshake started, or the process dying under you.

UDP does not play this game. A closed UDP port is supposed to answer with an ICMP port-unreachable, but firewalls drop ICMP as a matter of habit, so silence means either “nothing is there” or “it is fine and the service simply had nothing to say”. Tools like nc -vzu are guessing. Test UDP services with a real client — dig @host for DNS, ntpdate -q for NTP — not a port probe.

And if the error is “could not resolve host”, stop looking at ports entirely. That is DNS, it failed before a packet was ever addressed, and no firewall rule you write will change it.

Questions people ask

What port does PostgreSQL use?

5432/TCP, and MySQL or MariaDB use 3306/TCP. Those are defaults rather than requirements — a PgBouncer pooler usually answers on 6432, and any of them can be moved in config. If the port is open but the connection is still rejected, that is Postgres working correctly: listen_addresses and pg_hba.conf are separate gates and both have to allow you.

Why do I need root to bind to port 80?

On Unix, ports below 1024 are privileged: only root, or a process holding the CAP_NET_BIND_SERVICE capability, is allowed to bind them. The rule exists so that any user on a shared machine cannot impersonate a system service. You do not have to run as root to get there — grant AmbientCapabilities=CAP_NET_BIND_SERVICE in the systemd unit, lower net.ipv4.ip_unprivileged_port_start on Linux, or bind 8080 and let a reverse proxy or a port mapping own 80.

How do I find out which process is using a port?

On Linux, ss -tulpn lists every listening socket with its process, though you need sudo to see processes you do not own. On macOS the equivalent is lsof -nP -iTCP:8080 -sTCP:LISTEN. On Windows, netstat -ano | findstr :3000 gives you a PID to look up with tasklist. If nothing shows up, the listener is probably inside a container — docker port tells you what was actually published and on which interface.

What is the difference between connection refused and connection timed out?

Refused means a TCP RST came back: your packet reached the host and either nothing was listening on that port or a firewall explicitly rejected it. Timed out means nothing came back at all, so packets are being dropped somewhere — a security group, a DROP rule, the wrong IP, or a missing route. Refused points at the service, timed out points at the network in between.

Is it safe to expose Redis or the Docker socket to the internet?

No, and both are scanned continuously. An open Docker API on 2375 is unauthenticated root on the host: one request can start a container with the filesystem mounted. An open Redis on 6379 can be told to write its dump file into an SSH authorized_keys file. Bind them to 127.0.0.1 or a private interface and reach them through an SSH tunnel or a VPN instead.

What is the ephemeral port range?

It is the pool the kernel draws from for the source port of outbound connections. IANA suggests 49152–65535, Linux defaults to 32768–60999 via net.ipv4.ip_local_port_range, and Windows uses the IANA range. It starts to matter when a busy proxy or NAT gateway exhausts the pool and connections fail with “cannot assign requested address” — the fixes are widening the range, reusing TIME_WAIT sockets, or spreading traffic over more source addresses.

Related

Keep reading