@dcasota @jeremias.tx
Yes, I was suspecting that. The only thing is, I have tried the actual IP address of eth0 as well as the registry container IP in the name, instead of localhost. So instead of a tag of localhost, it was 172.26.58.184, or 172.17.0.2 and also adding whichever one I was trying to the docker.json insecure registries list. Didn’t seem to work either but as Jeremia pointed out I hadn’t pushed it to the registry, it was just in the repository. (Doh!) So, I will try your suggestions and see.
One other thing I don’t get is my personal laptop was a fresh install as of February/March of this year. So it should have worked the same as Jeremia’s, but it didn’t, AND in the logs that he had it said it was connecting via https, which makes no sense.
Steve
P.S.
You are correct. the quick test confirms your hypothesis. Also, since I had to setup the override of what docker was listening on, I know it’s only 127.0.0.1:5000 and 172.17.0.2:5000 . The default is only the unix socket, tcp://[::]:2376.
Hi Steve,
Thank you for the quick reply — looking forward to what curl -v http://[::1]:5000/v2/ returns; that one command settles the diagnosis.
Two clarifications on the puzzles you raised:
“Fresh Feb/Mar install should behave like Jeremias’s Jul install” — not necessarily. Between those dates there’s ~3 months of Windows Update, wsl --update (Store WSL pulls new networking behavior fairly often, and there were several nat/mirrored issues in the last 12 months), apt upgrade, and Docker Engine minor bumps — you’re on 29.6.1, Jeremias on 29.3.1. Any of those layers can shift IPv6/loopback handling. The only real apples-to-apples check is wsl --version and docker version on both machines.
“Why does Jeremias’s log show https://?” — there are two independent HTTPS conversations in that log, easy to conflate:
https://127.0.0.1:22376 "POST /v1.44/images/create…"— that’s the TCB Python client talking to the DinD daemon’s Engine API. DinD auto-generates a cert (seeDOCKER_TLS_CERTDIR=…earlier in the log) so its API port is TLS by design. Unrelated to the registry.- The
DinD>lines mentioninghttps://localhost:5000/v2/are the registry attempt from DinD’s Docker daemon. Docker’s registry client always probes HTTPS first, even for registries in the insecure-registry list;--insecure-registryonly enables the HTTP fallback after the probe. Both your log and Jeremias’s log show that first HTTPS probe. The difference is that on his box the HTTP fallback succeeds silently, while on yours the probe dies withECONNREFUSEDon[::1]:5000— and the HTTP retry hits the same[::1]and fails identically, which is why you also saw anhttp://localhost:5000/…line with the same error.
So HTTPS in the log ≠ “the registry was actually reached over HTTPS.”
Best,
Daniel
That ps output lines it up. The registry container is publishing v4-only (127.0.0.1:5000 plus the container-internal 172.17.0.2:5000), so [::1]:5000 genuinely has no listener, which is exactly why the DinD daemon’s IPv6 attempt gets ECONNREFUSED.
That gives you a fourth, cleanest option in addition to A/B/C: fix the publish so the registry listens on both families. Recreate the registry without the 127.0.0.1: prefix (and remove any -p [::1]:… too):
docker rm -f registry
docker run -d -p 5000:5000 --restart always --name registry registry:2
docker push localhost:5000/hello-world:latest # re-push, the old one was on the deleted registry
docker ps should now show 0.0.0.0:5000->5000/tcp, [::]:5000->5000/tcp. Then re-run bundle — DinD’s ::1 attempt should now succeed and no IPv6 disabling is needed.
@dcasota Thank you for your input here it is much appreciated.
This would explain why the issue was isolated and not reproducible. As I initially suspected at the start, the behavioral difference here was due to a configuration difference in @EvetsMostel1 environment.
In the end the issue had nothing to do specifically with the TorizonCore Builder tool itself.
Best Regards,
Jeremias
Hi @jeremias.tx ,
Well, the internal changes to the WSL2 network architecture have caused quite a bit of trouble over the past twelve months including issue tracking, PRs, feedback, etc. - hence the learning curve. I hope @EvetsMostel1 has enough information to solve the issue and start utilizing that lab environment as well.
Best wishes,
Daniel
So Option A didn’t work. I couldn’t see where it was trying to ever connect to the registry, mostly spent time trying to connect to 172.17.0.4:22376 max retries exceeded. Ended up with a lot of exceptions.
Option C
I can’t seem to push it. I tagged a new image in the repository 172.26.58.184:5000/myname:mytag (ip address of eth0) and when I tried to push it to the repository, I got a connection refused. I suspect this is a firewall issue. I put that address in the insecure registries and restarted docker. I don’t see how this is going to work though, it would seem that I would have to make a new registry that uses eth0 IP address. Maybe I am missing something?
Option B
I haven’t tried as didn’t want to mess with IPv6 on my work machine. I will try this on my home machine.
Hi Steve,
I think you replied to post #42 before seeing the ps content — take a look, it addresses your Option C symptom directly. Short version: your registry was launched with -p 127.0.0.1:5000:5000, so it only listens on 127.0.0.1:5000. Pushing/pulling against 172.26.58.184:5000 (or [::1]:5000) hits the same host but a different address that has no listener → connection refused. The fix is to recreate the registry so it binds all interfaces:
docker rm -f registry
docker run -d -p 5000:5000 --restart always --name registry registry:2
docker push 172.26.58.184:5000/myname:mytag # or localhost:5000/…
docker ps should now show 0.0.0.0:5000->5000/tcp, [::]:5000->5000/tcp. Once that’s in place, Option C (and the original localhost path, and IPv6) all resolve on their own — no need to touch WSL IPv6 at all.
One other observation from your Option A output: retrying 172.17.0.4:22376 max retries exceeded tells me your DinD is running on a docker0 bridge, not host networking. Jeremias’s working log shows Running DinD container: ports=None, network=host; yours doesn’t. If your tcb-env-setup.sh line lost the trailing -- --network=host at some point, that would explain it. When DinD is on its own bridge, localhost:5000 inside DinD is DinD’s own loopback, not the WSL host — the registry isn’t reachable regardless of IPv4/IPv6. Worth re-sourcing with:
source ./tcb-env-setup.sh -- --network=host
before retrying.
Best,
Daniel
@dcasota @jeremias.tx
Yes! That did work and the only thing I have to remember is to disable the registry unless I’m using it. IT would not like the fact that it is attached “all the time” to port 5000.
Thanks, so much Dan for helping sort this out. And thank you Jeremia for Sticking with this! I am very grateful.
It really helps to know what is going on behind the scenes. Even with the detailed logs it looked like something else to me.
Anyway, thank you both!
Steve
@dcasota
I get a usage message when I copied your line and ran it. I don’t think you can have a - by itself. I did get a new version on Monday, maybe that isn’t allowed anymore?
My bad. I apologize for the bad markdown formatting. It should be better now.
[quote=“dcasota, post:46, topic:30461”]
source ./tcb-env-setup.sh -- --network=host
[/quote]-
Yes, well that worked, but I got a message that said my script was out of date.
It says to use this wget comand, which puts the output text in the original tcb-env-setup.sh and creates a new tcb-env-setup.sh.x file where x is the next number in line, i.e. in my case the 4th time I have done this. This (tcb-env-setkup.sh.4) is the correctly downloaded file that should be put in tcb-env-setup.sh.
I believe this script should be:
wget -O tcb-env-setup.sh https://raw.githubusercontent.com/toradex/tcb-env-setup/master/tcb-env-setup.sh
Steve, if you just look at the source code you can see this was already fixed a month ago: Merge pull request #7 from lucasbrbz/fix/wget-wrong-flag · toradex/tcb-env-setup@bc6156f · GitHub
@jeremias.tx
Yes, and if I had bothered to look at the downloaded tcb-env-setup.sh file, I would have seen that. Sorry!
OK, this is the kind of thing that I have been dealing with all along. Things work one day and then don’t the next.
I had the “torizoncore-builder --verbose bundle --force --dind-param=“insecure-registry=http://localhost:5000” docker-compose.yml” working.
I needed to do it again because when I tried to get combine to work using build, I think it couldn’t find the file, even though it showed the sizes of the 3 different files in the bundle directory. So, I removed all the extra commented out #image: imagename lines in the docker-compose.yml file and tried to rerun the bundle command, and now I get this:
2026-07-16 18:58:45,318 - urllib3.connectionpool - DEBUG - http://localhost:None “GET /v1.55/containers/7deb74f4958e2ee23c40a86cac958054130d0184c81549739d7ba9373bab1d19/logs?stderr=1&stdout=1×tamps=0&follow=0&tail=all HTTP/1.1” 200 None
2026-07-16 18:58:45,320 - urllib3.connectionpool - DEBUG - http://localhost:None “GET /v1.55/containers/7deb74f4958e2ee23c40a86cac958054130d0184c81549739d7ba9373bab1d19/json HTTP/1.1” 200 None
2026-07-16 18:58:45,320 - torizon.tcbuilder.backend.bundle - DEBUG - DinD container logs: Certificate request self-signature ok
DinD> subject=CN = docker:dind server
DinD> /workdir/certs_20260716185843_098167.tmp/server/cert.pem: OK
DinD> Certificate request self-signature ok
DinD> subject=CN = docker:dind client
DinD> /workdir/certs_20260716185843_098167.tmp/client/cert.pem: OK
DinD> cat: can’t open ‘/proc/net/ip6_tables_names’: No such file or directory
DinD> cat: can’t open ‘/proc/net/arp_tables_names’: No such file or directory
DinD> iptables v1.8.10 (nf_tables)
DinD> time=“2026-07-16T18:58:44.281366927Z” level=info msg=“Starting up”
DinD> failed to load listeners: listen tcp 0.0.0.0:2376: bind: address already in use
DinD>
2026-07-16 18:58:45,321 - urllib3.connectionpool - DEBUG - http://localhost:None “POST /v1.55/containers/7deb74f4958e2ee23c40a86cac958054130d0184c81549739d7ba9373bab1d19/stop HTTP/1.1” 304 0
2026-07-16 18:58:45,341 - urllib3.connectionpool - DEBUG - http://localhost:None “DELETE /v1.55/containers/7deb74f4958e2ee23c40a86cac958054130d0184c81549739d7ba9373bab1d19?v=False&link=False&force=False HTTP/1.1” 204 0
2026-07-16 18:58:46,361 - urllib3.connectionpool - DEBUG - http://localhost:None “DELETE /v1.55/volumes/dind-volume HTTP/1.1” 204 0
2026-07-16 18:58:46,362 - root - CRITICAL - An unexpected Exception occurred. Please provide the following stack trace to
the Toradex TorizonCore support team:
2026-07-16 18:58:46,369 - root - ERROR - Traceback (most recent call last):
File “/usr/local/lib/python3.9/dist-packages/urllib3/connection.py”, line 204, in _new_conn
sock = connection.create_connection(
File “/usr/local/lib/python3.9/dist-packages/urllib3/util/connection.py”, line 85, in create_connection
raise err
File “/usr/local/lib/python3.9/dist-packages/urllib3/util/connection.py”, line 73, in create_connection
sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File “/usr/local/lib/python3.9/dist-packages/urllib3/connectionpool.py”, line 787, in urlopen
response = self._make_request(
File “/usr/local/lib/python3.9/dist-packages/urllib3/connectionpool.py”, line 488, in _make_request
raise new_e
File “/usr/local/lib/python3.9/dist-packages/urllib3/connectionpool.py”, line 464, in _make_request
self._validate_conn(conn)
File “/usr/local/lib/python3.9/dist-packages/urllib3/connectionpool.py”, line 1093, in _validate_conn
conn.connect()
File “/usr/local/lib/python3.9/dist-packages/urllib3/connection.py”, line 759, in connect
self.sock = sock = self._new_conn()
File “/usr/local/lib/python3.9/dist-packages/urllib3/connection.py”, line 219, in _new_conn
raise NewConnectionError(
urllib3.exceptions.NewConnectionError: HTTPSConnection(host=‘127.0.0.1’, port=22376): Failed to establish a new connection: [Errno 111] Connection refused
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File “/usr/local/lib/python3.9/dist-packages/requests/adapters.py”, line 589, in send
resp = conn.urlopen(
File “/usr/local/lib/python3.9/dist-packages/urllib3/connectionpool.py”, line 841, in urlopen
retries = retries.increment(
File “/usr/local/lib/python3.9/dist-packages/urllib3/util/retry.py”, line 535, in increment
raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type]
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host=‘127.0.0.1’, port=22376): Max retries exceeded with url: /version (Caused by NewConnectionError(“HTTPSConnection(host=‘127.0.0.1’, port=22376): Failed to establish a new connection: [Errno 111] Connection refused”))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File “/usr/local/lib/python3.9/dist-packages/docker/api/client.py”, line 223, in _retrieve_server_version
return self.version(api_version=False)[“ApiVersion”]
File “/usr/local/lib/python3.9/dist-packages/docker/api/daemon.py”, line 181, in version
return self._result(self._get(url), json=True)
File “/usr/local/lib/python3.9/dist-packages/docker/utils/decorators.py”, line 44, in inner
return f(self, *args, **kwargs)
File “/usr/local/lib/python3.9/dist-packages/docker/api/client.py”, line 246, in _get
return self.get(url, **self._set_request_timeout(kwargs))
File “/usr/local/lib/python3.9/dist-packages/requests/sessions.py”, line 602, in get
return self.request(“GET”, url, **kwargs)
File “/usr/local/lib/python3.9/dist-packages/requests/sessions.py”, line 589, in request
resp = self.send(prep, **send_kwargs)
File “/usr/local/lib/python3.9/dist-packages/requests/sessions.py”, line 703, in send
r = adapter.send(request, **kwargs)
File “/usr/local/lib/python3.9/dist-packages/requests/adapters.py”, line 622, in send
raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host=‘127.0.0.1’, port=22376): Max retries exceeded with url: /version (Caused by NewConnectionError(“HTTPSConnection(host=‘127.0.0.1’, port=22376): Failed to establish a new connection: [Errno 111] Connection refused”))
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File “/builder/torizoncore-builder”, line 234, in
mainargs.func(mainargs)
File “/builder/tcbuilder/cli/bundle.py”, line 132, in do_bundle
bundle(bundle_dir=args.bundle_directory,
File “/builder/tcbuilder/cli/bundle.py”, line 83, in bundle
bundle_be.download_containers_by_compose_file(
File “/builder/tcbuilder/backend/bundle.py”, line 663, in download_containers_by_compose_file
dind_client = manager.get_client()
File “/builder/tcbuilder/backend/bundle.py”, line 379, in get_client
dind_client = docker.DockerClient(base_url=self.docker_host, tls=tls_config)
File “/usr/local/lib/python3.9/dist-packages/docker/client.py”, line 45, in init
self.api = APIClient(*args, **kwargs)
File “/usr/local/lib/python3.9/dist-packages/docker/api/client.py”, line 207, in init
self._version = self._retrieve_server_version()
File “/usr/local/lib/python3.9/dist-packages/docker/api/client.py”, line 230, in _retrieve_server_version
raise DockerException(
docker.errors.DockerException: Error while fetching server API version: HTTPSConnectionPool(host=‘127.0.0.1’, port=22376): Max retries exceeded with url: /version (Caused by NewConnectionError(“HTTPSConnection(host=‘127.0.0.1’, port=22376): Failed to establish a new connection: [Errno 111] Connection refused”))
I made all the iptables entries to ACCEPT, but I still get this same output. This similar to the A version I was getting before I did the network=host. thing, I redid the source without that but still get the same result.
Any help would be greatly appreciated.
Steve
@dcasota
Well, I decided I better reboot. I went back to the version where I use eth0 IP address and removed and recreated the registry and repushed the 2 versions, localhost and the eth0 IP address. The localhost still didn’t work, but the eth0 one did. The localhost tried to connect with ipv6 but was refused.
Thanks Dan.
Hi Steve,
I’ve asked my “second brain” aka Anthropic Claude LLM and it gave me the following suggestions and files attached. Sorry for the delay.
Note up front: --network=host is kept — the port-2376 collision in post #53 is a leftover-process problem, not a host-networking problem.
Independent problems stacked on top of each other made the bundle fail.
+---+-------------------------+----------------------------------------+
| # | Symptom | Root cause |
+---+-------------------------+----------------------------------------+
| 1 | bind: address already | A prior crashed bundle run left a |
| | in use on 2376 | docker:dind process holding TCP/2376. |
| | | Because --network=host puts DinD in |
| | | the host netns (per the WSL2 setup |
| | | jeremias.tx recommended in post #8), |
| | | the collision is at the host level. |
| | | The reboot in post #54 cleared it. |
+---+-------------------------+----------------------------------------+
| 2 | localhost:5000 in the | localhost inside DinD is DinD's own |
| | compose image ref | loopback namespace, not the host's. |
| | refused over IPv6 | getaddrinfo picked ::1 first, nothing |
| | | listened, ECONNREFUSED. Registry only |
| | | reachable via routable host IP. |
+---+-------------------------+----------------------------------------+
| 3 | --dind-param value was | Missing -- on the inner flag and |
| | malformed anyway | Docker expects host:port, not a URL. |
+---+-------------------------+----------------------------------------+
The fix is: clear any leftover docker:dind from previous crashed runs (a reboot works, as post #54 showed), retag/push the image to the host’s routable IP, and pass a correctly-formed --dind-param.
Key invariants that make it work:
- Same string everywhere: the tag you push, the
--insecure-registryvalue, and theimage:field indocker-compose.ymlare all<host-ip>:5000— nolocalhost, nohttp://. (This is the piece post #54 confirmed:
the eth0-IP variant worked, the localhost variant still failed with an IPv6 refusal.) - Keep
--network=hostper post #8 guidance for WSL2. Don’t drop it. - Nothing else on 2376 before starting: verify with
ss -ltn | grep 2376(ornetstat -ano | findstr :2376on Windows) and remove any straydocker:dindcontainer from a previous crashed bundle. A reboot does the same thing.
Two files attached.
- toradex-bundle-fix.md — the analysis and summary of posts #53 and #54
- toradex-bundle-fix.sh — the automated fix script (WSL/Linux bash)
The script is parameterized via env vars (IMAGE_LOCAL, IFACE, REG_PORT, COMPOSE_FILE, TCB_ENV) so it doesn’t hard-code the user’s specifics. Copy it into the directory that has docker-compose.yml and tcb-env-setup.sh, chmod +x, and run. It re-sources tcb-env-setup.sh -- --network=host, matching post #8.
toradex-bundle-fix.sh (4.4 KB)
toradex-bundle-fix.md (6.0 KB)
Two new files documenting how to correctly write the registry address in each form (DNS / IPv4 / IPv6) — plus one working script that switches between them:
- toradex-bundle-addressing.md — reference: syntax table + one code block per form
- toradex-bundle-addressing.sh — same fix as before but parameterised:
ADDR_FORM=dns|ipv4|ipv6
toradex-bundle-addressing.sh (5.4 KB)
toradex-bundle-addressing.md (6.5 KB)
Quick preview of the actual byte-for-byte spellings
+------+---------------------+----------------------------------------+
| Form | Where | Value |
+------+---------------------+----------------------------------------+
| DNS | docker tag/push | myreg.lan:5000/app:1 |
| | compose image: | myreg.lan:5000/app:1 |
| | --insecure-registry | myreg.lan:5000 |
| | curl smoke test | http://myreg.lan:5000/v2/ |
+------+---------------------+----------------------------------------+
| IPv4 | docker tag/push | 192.168.6.6:5000/app:1 |
| | compose image: | 192.168.6.6:5000/app:1 |
| | --insecure-registry | 192.168.6.6:5000 |
| | curl smoke test | http://192.168.6.6:5000/v2/ |
+------+---------------------+----------------------------------------+
| IPv6 | docker tag/push | [fd00::10]:5000/app:1 |
| | compose image: | "[fd00::10]:5000/app:1" |
| | | (must quote in YAML) |
| | --insecure-registry | [fd00::10]:5000 |
| | curl smoke test | http://[fd00::10]:5000/v2/ |
+------+---------------------+----------------------------------------+
@dcasota
@
Hi Daniel,
When I run the “fix” version after I run the “addressing” version , I get:
./tcb-env-setup.sh: line 7: ZSH_EVAL_CONTEXT: unbound variable
Greping all the files I have in the local tcb directory, that variable is not found defined anywhere that I can see…
Thank you for all the explanations and the scripts! It helps a lot. My original instructions which I had before all this started, was to use the eth0 IP address, but I missed the step about pushing it to the registry which was part of my problem. The bundle command does work now though.
Steve
Hi Steve,
The following answer is from xAI Grok 4.5 . Give it a try.
Best regards,
Daniel
The issue is caused by the set -u / nounset option being active in your shell. The variable ZSH_EVAL_CONTEXT is a zsh-specific special parameter (not defined or set anywhere in the Toradex TCB files). It is only present when the code runs under zsh. zsh.sourceforge.io
When the shell has nounset enabled, expanding an unbound variable ($ZSH_EVAL_CONTEXT under bash, for example) produces exactly the error you saw. raw.githubusercontent.com
(The same pattern appears in many other scripts that try to detect the shell; the safe form is ${ZSH_EVAL_CONTEXT-} or ${ZSH_EVAL_CONTEXT:-}.)
Quick work-arounds
- Temporarily turn nounset off while sourcing (recommended):
set +u
source ./tcb-env-setup.sh # or . ./tcb-env-setup.sh
set -u
- Patch the local copy of the script (change the three checks that can be unbound):
if [ -n "${ZSH_EVAL_CONTEXT-}" ]; then
...
elif [ -n "${KSH_VERSION-}" ]; then
...
elif [ -n "${BASH_VERSION-}" ]; then
- Check whether nounset is on in your current shell:
echo $- # look for the letter 'u'
set -o | grep nounset
Other notes
• The script must be sourced (source tcb-env-setup.sh or . tcb-env-setup.sh). Running it as ./tcb-env-setup.sh is explicitly rejected later in the same block, and the alias / environment changes would not persist anyway. github.com
• Later in the same script the authors already use the safer form for another zsh variable (${ZSH_VERSION-}), so the top-of-file check is simply an oversight that surfaces only when set -u is active.
After applying one of the work-arounds the rest of the setup (Docker pull / alias creation for torizoncore-builder) should proceed normally.