5432
PostgreSQL 叢集監聽的地方——而當 psql 說連不上,原因不是它只綁回送位址、就是第二個叢集占著,再不然就是被 pg_hba.conf 擋下來。
照你的系統挑一行貼上去執行。它會直接印出占著這個連接埠的行程,以及它綁在哪個位址上——通常看到這兩件事答案就出來了。
sudo lsof -nP -iTCP:5432 -sTCP:LISTEN`-n` 讓位址維持數字,`-P` 讓 5432 不要被換成服務名稱,NAME 那一欄才讀得下去;`-iTCP:5432` 挑連接埠,`-sTCP:LISTEN` 只留監聽者。COMMAND 通常是 `postgres`,PID 是你要動手的號碼,USER 是它用的帳號,NAME 是綁定位址:`127.0.0.1:5432` 就是 `listen_addresses = 'localhost'` 這個預設值,也正是別台機器連不進來的原因,而 `*:5432` 代表每一張介面。完全沒有輸出、lsof 以 1 結束,代表沒有人在聽。不加 `sudo` 只看得到自己的行程,所以別人跑的 Postgres.app 或 Docker 發布出來的連接埠會看起來像空的——而 COMMAND 欄出現 `com.docker.backend` 就代表持有者是容器,要停容器而不是砍行程。
sudo ss -tlnp 'sport = :5432'`-t` 顯示 TCP socket,`-l` 只留監聽中的,`-n` 跳過服務名稱解析,`-p` 顯示使用每個 socket 的行程,`sport = :5432` 依來源埠過濾。什麼都先別看,先讀 Local Address:Port:`127.0.0.1:5432` 是原廠的 `listen_addresses` 預設值,`0.0.0.0:5432` 代表有人把它設成 `*` 或某個實際位址,`[::]:5432` 是 IPv6 萬用位址。持有者印成 `users:(("postgres",pid=1123,fd=6))`。裝了好幾個叢集的時候,把過濾條件放寬成 `'sport >= :5432 and sport <= :5435'`——第二個叢集跑在 5433 是這裡最常見的意外。net-tools 的寫法 `sudo netstat -tlnp | grep :543` 做同一件事,行程那欄改成斜線分隔的 pid/program;兩者都需要超級使用者權限才點得出不屬於你的行程。
netstat -ano | findstr ":5432"`-a` 顯示所有連線和正在監聽的連接埠,`-n` 讓位址和連接埠以數字表示,`-o` 在最後一欄放上所屬行程 ID。你要的那一行 State 是 LISTENING;Local Address 告訴你它是 `127.0.0.1:5432` 還是 `0.0.0.0:5432`,那就是「只有這台機器用得到」和「整個網路都用得到」的差別。用 `tasklist /FI "PID eq 4212"` 把 PID 換成名字——PID 這個過濾條件接受 eq、ne、gt、lt、ge、le——預期會看到 `postgres.exe`。`findstr` 比的是子字串,所以動手之前先確認那一欄真的是本機位址、而且真的結束在 5432。
Get-NetTCPConnection -LocalPort 5432 | Select-Object LocalAddress,LocalPort,State,OwningProcess它比對的是完整的連接埠而不是子字串,所以在附近號碼上裝了好幾個叢集時,這是比較安全的那一個。`State` 是 `Listen` 而 `LocalAddress` 是 `127.0.0.1`,就是只綁回送位址的情況;`0.0.0.0` 或 `::` 則是整個網路。把 `OwningProcess` 丟給 `Get-Process -Id` 就能拿到執行檔名稱。如果結果是空的、啟動卻還是失敗,那就是連接埠被保留而不是被占用——`netsh interface ipv4 show excludedportrange protocol=tcp` 會列出 Hyper-V、WSL2 和 Docker Desktop 在開機時搶下的範圍,那些範圍裡永遠不會有行程可以找。
`nmap -Pn -p 5432 db.example.com` 從用戶端實際所在的位置檢查連得到連不到,`-Pn` 跳過主機探索,所以會丟掉 ping 的主機照樣掃得到。三種狀態的意思很精確:`open` 代表目標上有應用程式在那個連接埠上聽;`closed` 代表主機回應了但沒有人在聽;`filtered` 代表防火牆、過濾器或其他網路障礙擋住探測,所以 nmap 分不出是哪一種。對 PostgreSQL 來說,`closed` 幾乎一定代表 `listen_addresses` 還是 `localhost`——伺服器跑得好好的,只是不在那張介面上——而 `filtered` 代表安全群組或主機防火牆把封包丟了。`--reason` 顯示是哪個封包決定了結論,`-sV` 讓 nmap 去辨識服務。只掃你自己負責的主機。
| 你看到的訊息 | 代表什麼 |
|---|---|
psql: error: connection to server at "db.example.com" (203.0.113.9), port 5432 failed: Connection refused — Is the server running on that host and accepting TCP/IP connections? | libpq 印出那句提示的時機,剛好就是核心拒絕了這條連線的時候,而問句的後半才是重點。主機活著而且回了 reset,所以這幾乎絕不是防火牆。要嘛叢集停了,要嘛它帶著 `listen_addresses = 'localhost'` 在跑,根本不在你撥的那張介面上。在資料庫主機上跑探測指令、讀綁定位址;如果上面寫 `127.0.0.1`,防火牆再怎麼調都沒有用。 |
psql: error: connection to server at "db.example.com" (203.0.113.9), port 5432 failed: Connection timed out | 完全沒有回應。這才是被拒絕那則訊息不是的那種情況:雲端安全群組、主機上的防火牆,或某條網路 ACL 把 SYN 丟掉而不是拒絕。被拒絕是立刻回來的,被丟掉要等滿整個連線逾時,所以等待本身就是訊號。這裡 `nmap --reason` 會報 `filtered`,上面那則被拒絕的則是 `closed`。 |
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory — Is the server running locally and accepting connections on that socket? | 連接埠從頭到尾都沒有參與。libpq 走了 Unix domain socket 路徑,因為 `host` 是空的或看起來像絕對路徑,而那裡沒有 socket 檔案——通常是因為伺服器跑在容器裡,或是你的用戶端建置時預期 `/tmp`,而打包好的伺服器把 socket 放在 `/var/run/postgresql`。加 `-h 127.0.0.1` 強制走 TCP,或是把 `-h` 指向真正放 socket 的那個目錄。 |
FATAL: no pg_hba.conf entry for host "10.0.0.5", user "app", database "appdb", no encryption | 這是這份清單上最好的一種失敗:網路、連接埠和伺服器全都沒問題,而 PostgreSQL 已經走到查它的主機認證規則、發現沒有一條符合的階段。注意最後那個欄位——它報的是加密狀態,所以 `no encryption` 對上一條要求 `hostssl` 的規則就是那個對不上的地方。在 `pg_hba.conf` 裡加一條或放寬一條再重新載入;網路那邊一根寒毛都不用動。 |
FATAL: password authentication failed for user "app" | 這也不是連接埠問題——交握完成了,是帳密被拒絕。密碼錯,或是 `pg_hba.conf` 指定了用戶端滿足不了的方法:只懂 `md5` 的老驅動程式,就算密碼是對的,碰上 `scram-sha-256` 那一行照樣過不了。`FATAL: Peer authentication failed for user "app"` 是它的兄弟,而且意思跟網路故障完全相反:你是從 Unix socket 進來的,在那裡 PostgreSQL 比對的是你的作業系統使用者名稱和資料庫角色。 |
LOG: could not bind IPv4 address "0.0.0.0": Address already in use — HINT: Is another postmaster already running on port 5432? If not, wait a few seconds and retry. | 這是伺服器自己對 `EADDRINUSE` 的說法,而那句提示是有實際作用的:如果真的有一個 postmaster 在跑,用你系統對應的探測指令去找,預期會是第二個叢集而不是什麼神祕的東西。如果根本沒有人在聽,那是上一個伺服器留下的 socket 還在 `TIME_WAIT`,連接埠很快會自己空出來——等它是一種解法,把新叢集開在 5433 也是。 |
could not bind IPv4 address: Permission denied | 這不是特權連接埠那條規則,那只管 1024 以下。在 Linux 主機上,這通常是 SELinux 或 AppArmor 拒絕了那支執行檔的綁定——最常見於把 PostgreSQL 搬到政策沒有標記過的非標準連接埠之後——再不然就是容器啟動時沒帶那個能力。去看稽核紀錄,不要看防火牆。在 Windows 上,同樣的形狀代表連接埠落在保留的排除範圍裡,那裡根本沒有持有者可以找。 |
The application connects but reads stale data, or a table it just created is missing | 兩個叢集,差一個連接埠。原地升級過的機器常常舊的大版本在 5432、新的在 5433,而一個省略連接埠的連線字串會安靜地連到 5432。在斷定複寫壞掉之前,先用 `SHOW port;` 和 `SHOW server_version;` 問這個 session 它到底在哪一個上面。 |
IANA 把 5432 登記給 `postgresql`,TCP 和 UDP 都有,而 PostgreSQL 只用 TCP 那一半。官方文件對號碼講得很直白——伺服器監聽的 TCP 連接埠預設是 5432——但真正的權威是跑著的那台伺服器,而 `SHOW port;` 就是問它的方法。這件事比聽起來重要,因為裝了兩個大版本的機器通常第二個叢集會在 5433,而一個看起來連錯資料庫的 `psql` session,十之八九是連對了連接埠、連錯了叢集。接著有兩個預設值決定誰碰得到它。`listen_addresses` 預設是 `localhost`,官方文件的說法是這只允許本機的 TCP/IP 回送連線,所以原廠設定的伺服器在有人動手改之前,從別台機器根本連不到;請用 `SHOW listen_addresses;` 問伺服器,不要去讀一份它可能根本沒載入的 `postgresql.conf`。另外,和 MySQL 不一樣,libpq 連線字串裡的 `localhost` 就是一個普通的 TCP 主機名稱:libpq 只有在 `host` 是空的、或看起來像絕對路徑時才會改走 Unix domain socket,而那時候這個值指的是放 socket 檔案的目錄——上游建置版是 `/tmp`,不過打包者常常會搬。最後,連得到連接埠只做完一半。`pg_hba.conf` 會依來源位址、使用者和資料庫,決定一條已經完成 TCP 交握的連線能不能再往前走一步,而這正是為什麼這麼多 5432 的問題最後給你的是伺服器錯誤,不是網路錯誤。
不要把 5432 對外開。上游的預設值本來就站在你這邊——`listen_addresses` 是 `localhost`,所以一台從網際網路連得到的 PostgreSQL 一定是有人刻意打開的——而通常跟著一起被打開的,是一條寬到整個世界都符合的 `pg_hba.conf`。連接埠一旦碰得到,還有兩個預設值會立刻變得重要。`ssl` 預設是 `off`,所以除非有人啟用過,這個協定就是明文在跑,密碼和查詢結果都算在內。而 libpq 的 `sslmode` 預設是 `prefer`,文件寫的是「先試 SSL 連線,失敗就試非 SSL 連線」——這是機會性加密,什麼都不驗證,而且會安靜地降級,所以它不可能察覺中間有人在聽。如果資料庫真的必須從別的地方連進來,就走 SSH 通道、放進私有子網路,或在防火牆上依來源位址限制。`pg_hba.conf` 裡要求 `scram-sha-256` 而不是 `md5`,而且除了 socket 以外的任何地方都不要用 `trust`。用戶端這邊,`verify-ca` 會沿著憑證鏈一路驗到用戶端存放的根憑證,`verify-full` 還會再確認伺服器主機名稱和憑證裡的名稱相符——`verify-full` 搭配 `sslrootcert` 是唯一擋得住中間人的組合。
這些全都在你的瀏覽器裡跑,不會上傳任何東西。
查 5432 的時候,通常也會順手看一下這幾個。
幾乎都是權限問題。lsof 和 ss 會列出 socket,但別人的行程它不會顯示所有者,所以由系統帳號、容器執行環境或 launchd 啟動的服務,在你補上 sudo 之前就只是一個沒有主人的監聽項。Windows 則是反過來:netstat 什麼都沒有,綁定卻還是失敗,那代表這個號碼落在 Hyper-V、WSL2 或 Docker Desktop 開機時預留的範圍裡。
先看清楚它是什麼。沒收乾淨的開發伺服器砍掉沒事;正在寫入的資料庫不行,其他東西依賴的系統服務也不行。如果持有者是容器,請停容器,不要砍主機看到的那個行程——砍了執行環境只會把它拉起來。遇到真的不能動的,把自己的服務換一個連接埠反而更快。
因為它綁在 127.0.0.1 而不是 0.0.0.0。每一頁的指令之所以會把綁定位址跟行程名一起印出來,就是為了這件事:綁在回送位址上的服務,不管防火牆怎麼設,都只有這台機器自己連得到。現在不少框架是刻意這樣預設的,所以通常是一個參數的問題,不是 bug。如果已經是 0.0.0.0 還是不通,下一個要懷疑的就是主機防火牆或雲端的安全群組。
幾乎沒有幫助。掃全網的機器持續在掃整段範圍,換號碼買到的是幾個小時,不是安全,代價卻是每個同事都得記住它。它確實能讓針對預設埠的無差別掃描少污染一點日誌,這算是實際但有限的好處。真正會改變結果的是認證、限制來源位址的防火牆,以及一開始就不要讓它對外。
連接埠 5432 還是卡住嗎?看完整的連接埠對照表,或是回到上面,從你實際看到的那行錯誤往回追。