Fix curl getaddrinfo() thread failed to start in Docker
Why curl fails to resolve a host in a RUN step on one server and not on your laptop, how to prove it is the engine and not the image, and which fix to pick.
TL;DR#
curl: (6) getaddrinfo() thread failed to start in a Docker RUN step means curl could not create the thread it resolves host names in. The image is not broken: a recent glibc inside it first tries the clone3 system call and only falls back to the older clone call when the kernel answers "not implemented" (ENOSYS). Docker Engine releases before 20.10.10 shipped a seccomp profile with no rule for clone3, so the container got "operation not permitted" instead, and glibc gave up. The fix is to upgrade Docker Engine on the machine that fails; if you cannot, pin an older tag of the base image, published before it moved to a newer distribution, as the accepted answer did.
The error in the build log#
The Stack Overflow question builds FROM linuxserver/code-server:latest and installs Node.js 14 from NodeSource. The build works on Windows 11 and fails on an Ubuntu server:
Step 2/2 : RUN curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - && apt-get install -y nodejs
---> Running in d0ec2365c9d1
curl: (6) getaddrinfo() thread failed to start
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package nodejs
The command '/bin/sh -c curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - && apt-get install -y nodejs' returned a non-zero code: 100Two errors are printed, but only the first one matters. E: Unable to locate package nodejs is a consequence, explained further down.
Why it happens#
curl resolves host names on a separate thread. Its threaded resolver, lib/asyn-thread.c, prints exactly this message when it cannot start that resolver thread:
failf(data, "getaddrinfo() thread failed to start");So the question is why a container cannot create a thread. The answer is in glibc and in Docker's seccomp filter, and three primary sources line up:
-
glibc tries clone3 first. In glibc's
clone-internal.c, the new call is attempted and the old one is used only if the error is ENOSYS. Any other error is returned to the caller:cint ret = __clone3_internal (cl_args, func, arg); if (ret != -1 || errno != ENOSYS) return ret; -
Docker's old default profile answered clone3 with EPERM. The moby commit that fixed it, 9f6b562 (PR #42681), explains that the built-in policy had no rule for clone3, so runc's fallback returned "the historical EPERM errno", and that "when docker seccomp policy triggers EPERM from clone3, no fallback occurs and programs are thus unable to spawn threads." The fix adds a rule that returns ENOSYS (errno 38) for clone3, unless the container has CAP_SYS_ADMIN.
-
The fix reached Docker Engine 20.10.10. The 20.10 release notes list, under 20.10.10 (25 October 2021): "Add support for clone3 syscall in the default seccomp policy to support running containers based on recent versions of Fedora and Ubuntu", via the backport moby/moby#42836.
That explains "works on my laptop". The seccomp profile belongs to the engine, not to the image, and Docker's seccomp page says that a container "uses the default profile unless you override it with the --security-opt option". The question does not give either engine version, but the pattern fits: the same Dockerfile, a failure only on the server, and a fix by pinning an older base tag. The image had changed too. The code-server image changelog records "Rebase to jammy" on 29 September 2022, three months before the question. An image on Ubuntu 22.04 brings a glibc that uses clone3, and the old profile on the server stopped it.
A user in adoptium/containers#215 hit the same message with Ubuntu 22.04 images. The comment there shows how to load the pre-fix profile on purpose to see it, which is how it was reproduced below.
Reproduce it on a current engine#
You do not need an old server to see it. The profile can be loaded per container with --security-opt seccomp=<file>. This was reproduced on 24 October 2026 on Docker Engine 29.7.2, with docker run (not a build), using the node:22-bookworm image (Debian 12, glibc 2.36, curl 7.88.1):
# the default profile just before the clone3 rule, and right after it
curl -sL https://raw.githubusercontent.com/moby/moby/c7cd1b9436ac381747a5c52dddac5a66f97c61f8/profiles/seccomp/default.json -o before_clone3.json
curl -sL https://raw.githubusercontent.com/moby/moby/9f6b562dd12ef7b1f9e2f8e6f2ab6477790a6594/profiles/seccomp/default.json -o after_clone3.json
docker run --rm --security-opt seccomp=before_clone3.json node:22-bookworm \
curl -fsSL https://deb.nodesource.com/setup_22.x -o /dev/nullWith the old profile, the exact error from the question, exit code 6:
curl: (6) getaddrinfo() thread failed to start
exit=6The two JSON files differ by one thing: the clone3 entries. Swapping before_clone3.json for after_clone3.json, and nothing else, gives:
exit=0The same image with no --security-opt at all, on the current engine, also exits with 0.
Fix#
1. Check the engine version on the machine that fails#
docker version --format '{{.Server.Version}}'Run it on both machines. If the failing one reports a version older than 20.10.10, that is the cause; the newer one is the reference.
2. Upgrade Docker Engine#
This is the fix that keeps working as base images move forward. On Ubuntu, follow Docker's install page for Ubuntu. It warns that "Your Linux distribution may provide unofficial Docker packages", lists docker.io among them, and has you remove them before installing Docker's own packages:
sudo apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)
# then add Docker's apt repository as the page describes, and:
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginThe adoptium commenter quotes another user for whom updating to docker-engine 20.10.16 resolved it. Whatever the exact release, check that the engine version has moved past 20.10.10, then rebuild.
3. If you cannot touch the engine: pin an older base image#
The accepted answer did this. It replaced latest with the tag 4.4.0-ls125, published on 12 June 2022, before the image's rebase to jammy:
FROM linuxserver/code-server:4.4.0-ls125
RUN curl -fsSL https://deb.nodesource.com/setup_14.x | sudo -E bash - &&\
sudo apt-get install -y nodejsIt works, but it freezes you on old packages. Treat it as a stopgap until the engine is upgraded.
4. Diagnostic only: seccomp=unconfined#
The most-voted answer runs the container with --security-opt seccomp=unconfined. That removes the filter, so the error disappears; reproduced on the same image, it exits with 0. Docker's seccomp page is explicit, though: "It is not recommended to change the default seccomp profile." Use it to confirm that seccomp is the cause, not as the fix.
That answer's Compose snippet also does less than it seems for a build. In the Compose specification, security_opt is a container setting, and the build section has no such key. It applies to the container Compose starts, not to the RUN steps of the image build that failed in the question.
Verify the fix#
Run a container from the same image, with no security options, on the upgraded engine:
docker run --rm node:22-bookworm curl -fsSL https://deb.nodesource.com/setup_22.x -o /dev/null && echo okThen rebuild your image without the layer cache, so the RUN step really runs again. The build log should no longer show the curl: (6) line or Unable to locate package nodejs.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Why apt-get ran anyway: the pipe hides curl's failure#
In the question, apt-get ran after curl had already failed. In curl … | bash - && apt-get … the && tests the exit status of the pipeline, and the shell reports the status of the last command in the pipe, which is bash. bash received an empty script and succeeded. Reproduced with the old profile:
curl: (6) getaddrinfo() thread failed to start
pipeline-exit=0So the NodeSource repository was never added, and apt-get install -y nodejs searched the distribution's own lists, which is where E: Unable to locate package nodejs and exit code 100 come from. When a build stops at "Unable to locate package" after a curl | bash installer, look one line up first.
Node itself crashes under the same profile#
The failure is not specific to curl. Anything that creates threads through a recent glibc is affected, and Node.js creates its worker threads at startup. Under the old profile, node:20-bookworm-slim (glibc 2.36) does not even print its version:
# Assertion failed: (0) == (uv_thread_create(t.get(), start_thread, this))
exit=139The same Node.js major on node:20-bullseye-slim (Debian 11, glibc 2.31), under the same old profile, starts normally:
v20.20.2
exit=0The Node.js major did not change between the two runs; the Debian release did, and with it the glibc. This dependency follows the distribution, not the runtime, which is why moving from a Debian 11 image to a Debian 12 one can surface this on an old server without any change to your code. A FROM image you do not control can change under you in other ways too, and some of its settings do not behave as you might expect: a base image's ENTRYPOINT, for instance, is replaced by yours rather than extended (override or remove a base image's ENTRYPOINT).
This is a cousin of other "fine on my machine, broken in the container" failures that come from the kernel or the runtime rather than from your code. esbuild's ETXTBSY error during npm install came from libuv's io_uring use on some kernels, and a Next.js build that fails only inside Docker came from Node 18's native fetch() in the image. With this one, start by comparing the engine versions on the two machines. If the failing engine predates 20.10.10, upgrading it fixes curl, apt and Node together, and you can keep the current base image.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix Docker npm EBADENGINE: Unsupported engine
Read the Required and Actual lines, distinguish npm self-upgrades from application installs, and stop a floating npm release breaking an unchanged Dockerfile.
Fix esbuild spawnSync ETXTBSY in Docker: Node 20.3 io_uring
Why npm install of esbuild started failing with ETXTBSY on node:20.3 images in June 2023, what changed in libuv and Docker since, and the build fix that holds today.
Docker: Run a Shell Command Before ENTRYPOINT at Startup
Your envsubst step ran while the image was built, long before the container got its variables. A small entrypoint script runs it at start-up instead and still hands control to npm or node.


