{"record":{"id":"46b1c4fe8a1631a4","repo":"dagger/dagger","slug":"checking-for-port-d-s-w","errorCode":null,"errorMessage":"checking for port %d/%s: %w","messagePattern":"checking for port (.+?)/(.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/healthcheck.go","lineNumber":85,"sourceCode":"\t\t\t\t// NB(vito): it's a _little_ silly to dial a UDP network to see that it's\n\t\t\t\t// up, since it'll be a false positive even if they're not listening yet,\n\t\t\t\t// but it at least checks that we're able to resolve the container address.\n\t\t\t\tconn, err := dialer.Dial(\n\t\t\t\t\tport.Protocol.Network(),\n\t\t\t\t\tnet.JoinHostPort(d.host, fmt.Sprintf(\"%d\", port.Port)),\n\t\t\t\t)\n\t\t\t\tif err != nil {\n\t\t\t\t\tslog.Warn(\"port not ready\", \"error\", err, \"elapsed\", retry.GetElapsedTime())\n\t\t\t\t\treturn \"\", err\n\t\t\t\t}\n\n\t\t\t\tendpoint := conn.RemoteAddr().String()\n\t\t\t\t_ = conn.Close()\n\t\t\t\treturn endpoint, nil\n\t\t\t})\n\t\t}, backoff.WithContext(retry, ctx))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"checking for port %d/%s: %w\", port.Port, port.Protocol.Network(), err)\n\t\t}\n\n\t\tslog.Info(\"port is healthy\", \"endpoint\", endpoint)\n\t}\n\n\treturn nil\n}\n\ntype containerProcessExecutor interface {\n\tExec(context.Context, string, executor.ProcessInfo) error\n}\n\ntype dockerHealthcheck struct {\n\targs   []string\n\torigin trace.SpanContext\n\tctr    *Container\n\texec   containerProcessExecutor\n\tsvcID  string","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/dagger/dagger/blob/82ba2681dbe30d3547a1dc50ea495900ab5b6047/core/healthcheck.go#L67-L103","documentation":"Raised in portHealthChecker.Check after the exponential-backoff retry loop exhausts (or the context is cancelled) while trying to dial host:port inside the service's network namespace. It wraps the last dial error, meaning the container service port never became reachable within the backoff budget. Typically the wrapped error is connection refused, i/o timeout, or context deadline exceeded.","triggerScenarios":"Starting a Dagger service (Container.up / service start) with a port whose ExperimentalSkipHealthcheck is false, where net.Dial to the container address fails for every retry until backoff gives up or ctx is done.","commonSituations":"The server inside the container crashed or never started, the app listens only on 127.0.0.1 instead of 0.0.0.0, wrong port mapping (container listens on 8080 but exposes 80), slow startup exceeding backoff limits, or cancelled context from an upstream timeout.","solutions":["Fix the server inside the container to bind 0.0.0.0 (not localhost) on the exposed port.","Verify the exposed port number and protocol (tcp/udp) match what the app listens on.","Check service logs for startup crashes; fix the startup failure or increase startup time.","Set ExperimentalSkipHealthcheck(true) only if the port is UDP or intentionally uncheckable.","If it's just slow startup, retry with a longer context deadline."],"exampleFix":"// before\nctr.WithExposedPort(80).AsService().Up(ctx) // app listens on 127.0.0.1:80\n// after\nctr.WithExposedPort(80).WithExec([], ContainerWithExecOpts{SkipEntrypoint: false}) // app binds 0.0.0.0:80\n// or, for UDP services:\nWithExposedPort(53, ContainerWithExposedPortOpts{Protocol: Udp, ExperimentalSkipHealthcheck: true})","handlingStrategy":"retry","validationCode":"// verify the app binds on 0.0.0.0 and the port is correct\n// e.g. inside the image: netstat -ltn | grep 0.0.0.0:8080","typeGuard":"var dialErr *net.OpError\nif errors.As(err, &dialErr) && errors.Is(dialErr.Err, syscall.ECONNREFUSED) {\n\t// port closed: server not listening or wrong port\n}","tryCatchPattern":"err := svc.Up(ctx)\nif err != nil && strings.Contains(err.Error(), \"checking for port\") {\n\tvar opErr *net.OpError\n\tif errors.As(err, &opErr) && errors.Is(opErr.Err, syscall.ECONNREFUSED) {\n\t\treturn fmt.Errorf(\"service never listened on the exposed port; check bind address (0.0.0.0) and logs: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Bind service processes to 0.0.0.0, never 127.0.0.1, inside containers","Confirm exposed port numbers/protocols match the app configuration","Check container/service logs for startup crashes before assuming a network bug","Use ExperimentalSkipHealthcheck(true) for UDP or non-TCP-listening services","Allow generous context deadlines for slow-starting services"],"tags":["network","healthcheck","service","timeout"],"backgroundTag":"service-port-unreachable","analyzedSha":"82ba2681dbe30d3547a1dc50ea495900ab5b6047","analyzedAt":"2026-09-05T07:21:37.930Z","contentChangedAt":"2026-09-05T07:21:37.930Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}