From 83aa3e5e2e7ae74240f6e8176df4d31ee569905a Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 25 Mar 2026 09:04:45 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20serve=20default=20bind=20address=20127.0?= =?UTF-8?q?.0.1=20=E2=86=92=200.0.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit zeroboot serve previously bound to 127.0.0.1, which breaks container environments where health probes and traffic arrive on the Pod/container IP rather than localhost: - Kubernetes: kubelet readiness/liveness probes connect from Pod IP - Docker: health checks connect via bridge network - Any reverse proxy or load balancer forwarding to the container Change the default bind to 0.0.0.0 (all interfaces) and add a --bind flag so users can still restrict to localhost for local development: zeroboot serve python:/workdir/python 8080 # 0.0.0.0 (default) zeroboot serve python:/workdir/python 8080 --bind 127.0.0.1 # localhost only The ZEROBOOT_BIND env var in the Docker entrypoint passes through to this flag. --- src/main.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 527fa70..46f5055 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,7 +29,7 @@ fn main() -> Result<()> { eprintln!( " test-exec - Test executing a command in a fork" ); - eprintln!(" serve [port] - Start API server"); + eprintln!(" serve [port] [--bind addr] - Start API server (default bind: 0.0.0.0)"); Ok(()) } } @@ -390,10 +390,27 @@ fn load_api_keys() -> Vec { fn cmd_serve(args: &[String]) -> Result<()> { if args.len() < 1 { - bail!("Usage: zeroboot serve [,lang:workdir2,...] [port]"); + bail!("Usage: zeroboot serve [,lang:workdir2,...] [port] [--bind ]"); } let port: u16 = args.get(1).and_then(|p| p.parse().ok()).unwrap_or(8080); + // Parse optional --bind flag (default 0.0.0.0 for container/K8s compatibility). + // K8s health probes connect from the Pod IP (not localhost), and Service ClusterIP + // routing requires the server to listen on all interfaces. + // For local dev, use: --bind 127.0.0.1 + let bind_addr = { + let mut addr = "0.0.0.0".to_string(); + let mut i = 2; + while i + 1 < args.len() { + if args[i] == "--bind" { + addr = args[i + 1].clone(); + break; + } + i += 1; + } + addr + }; + // Parse workdir specs: "workdir" or "python:workdir1,node:workdir2" let mut templates = std::collections::HashMap::new(); for spec in args[0].split(',') { @@ -430,10 +447,10 @@ fn cmd_serve(args: &[String]) -> Result<()> { .route("/v1/metrics", axum::routing::get(metrics_handler)) .with_state(state); - let listener = tokio::net::TcpListener::bind(format!("127.0.0.1:{}", port)) + let listener = tokio::net::TcpListener::bind(format!("{}:{}", bind_addr, port)) .await .unwrap(); - eprintln!("Zeroboot API server listening on port {}", port); + eprintln!("Zeroboot API server listening on {}:{}", bind_addr, port); axum::serve( listener, app.into_make_service_with_connect_info::(),