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::(),