{"record":{"id":"dc39802513d6b10f","repo":"cloudflare/pingora","slug":"failed-to-build-listeners","errorCode":null,"errorMessage":"Failed to build listeners","messagePattern":"Failed to build listeners","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"pingora-core/src/services/listening.rs","lineNumber":293,"sourceCode":"}\n\n#[async_trait]\nimpl<A: ServerApp + Send + Sync + 'static> ServiceTrait for Service<A> {\n    async fn start_service(\n        &mut self,\n        #[cfg(unix)] fds: Option<ListenFds>,\n        shutdown: ShutdownWatch,\n        listeners_per_fd: usize,\n    ) {\n        let runtime = current_handle();\n        let endpoints = self\n            .listeners\n            .build(\n                #[cfg(unix)]\n                fds,\n            )\n            .await\n            .expect(\"Failed to build listeners\");\n\n        let app_logic = self\n            .app_logic\n            .take()\n            .expect(\"can only start_service() once\");\n        let app_logic = Arc::new(app_logic);\n\n        let mut handlers = Vec::new();\n\n        endpoints.into_iter().for_each(|endpoint| {\n            for _ in 0..listeners_per_fd {\n                let shutdown = shutdown.clone();\n                let my_app_logic = app_logic.clone();\n                let endpoint = endpoint.clone();\n\n                let jh = runtime.spawn(async move {\n                    Self::run_endpoint(my_app_logic, endpoint, shutdown).await;\n                });","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/cloudflare/pingora/blob/0046038bd402bc82912da862dadf9a479f31e9f1/pingora-core/src/services/listening.rs#L275-L311","documentation":"When a listening service starts, pingora binds every configured endpoint via listeners.build(fds) and expects success. build() creates the actual TCP/UDS listeners (TransportStackBuilder::build -> builder.listen), adopts systemd-upgrade fds when present, and constructs TLS settings. Any failure — address already in use, permission denied on privileged ports, bad unix socket path, or invalid TLS settings — aborts the whole service startup with this message.","triggerScenarios":"run_service()/service start with: a bind port already taken; binding port <1024 without CAP_NET_BIND_SERVICE; a unix socket path that exists or sits in a non-writable directory; TLS endpoints with invalid/unreadable cert or key material; zero-cost upgrade path where passed ListenFds don't match configured listeners.","commonSituations":"An old process or second instance still holding the port; docker port-publish conflicts; missing CAP_NET_BIND_SERVICE in containers for 80/443; relative cert paths breaking when cwd changes under systemd; stale socket files left from a crash.","solutions":["Find what holds the address: `ss -ltnp | grep <port>` or `lsof -i :<port>`; stop the old process or change the bind address/port","For privileged ports grant CAP_NET_BIND_SERVICE (`setcap cap_net_bind_service=+ep ./binary`) or use a front proxy / net.ipv4.ip_unprivileged_port_start","Verify every TLS cert/key path is absolute, readable by the service user, and parses (`openssl x509 -in cert -noout`, `openssl pkey -in key -noout`)","Remove stale unix socket files, or ensure the parent directory is writable by the service user","With systemd socket activation, make sure the number of passed fds matches the configured listeners"],"exampleFix":"# before: privileged port without capability, or port already in use\n./my_proxy --daemon -d 0.0.0.0:80,0.0.0.0:443   # panics: Failed to build listeners\n\n# after: grant bind capability (or move to an unprivileged port)\nsudo setcap cap_net_bind_service=+ep ./my_proxy\n./my_proxy --daemon -d 0.0.0.0:80,0.0.0.0:443","handlingStrategy":"validation","validationCode":"use std::net::TcpListener;\n\n// Validate at config-load time, before run_service()\nfn endpoints_bindable(addrs: &[std::net::SocketAddr]) -> bool {\n    addrs.iter().all(|a| TcpListener::bind(a).is_ok())\n}\n\n// Also pre-parse TLS material so bad certs fail loudly, not in listeners.build()\nfn tls_material_ok(cert: &std::path::Path, key: &std::path::Path) -> bool {\n    std::process::Command::new(\"openssl\")\n        .args([\"x509\", \"-in\"]).arg(cert).arg(\"-noout\").status().map(|s| s.success()).unwrap_or(false)\n    // repeat for the key with `openssl pkey -in key -noout`\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Pre-flight bind every configured address during config validation so errors surface before run_service","Keep exactly one service instance per port; monitor for stale processes holding sockets","Use absolute cert/key paths and test-parse them at startup","For privileged ports, grant CAP_NET_BIND_SERVICE to the binary rather than running as root"],"tags":["rust","pingora","listener","bind","address-in-use","tls","startup","panic"],"backgroundTag":"address-in-use","analyzedSha":"0046038bd402bc82912da862dadf9a479f31e9f1","analyzedAt":"2026-08-16T21:33:22.341Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}