>_C2CZ
Serverless · Self-Hosting · Security · Independent Ops

Docker driver failed programming external connectivity fix

Your container will not start, and the daemon answers with driver failed programming external connectivity on endpoint … Docker could not wire the published host port to the container, so the run or compose up fails before your application ever boots. The useful part of the error is the tail after the endpoint ID, and it comes in three flavors — each one points at a different cause and a different fix.

I burned a Friday-night redeploy on this once. The compose file was unchanged, the port map was correct, and ss showed the port as free — yet every start attempt died with the same message. The container that owned the port was still running, just in another project I had forgotten about, and its docker-proxy was the process answering on the socket. That is the shape of this error: the daemon's message is generic on purpose, and the tail tells you which layer refused the port mapping.

TL;DR

  • The error is a port-publishing failure, not an application error. Docker failed to bind a host port to the container, so nothing in your image or entrypoint is the problem.
  • Read the tail. bind: address already in use or Failure EADDRINUSE = a host process owns the port. port is already allocated = Docker's own allocator thinks another container holds it. iptables: No chain/target/match by that name = the firewall rules Docker needs were flushed.
  • Diagnose before restarting anything: ss -tlnp for host listeners, docker ps -a for the invisible container, and only then sudo systemctl restart docker as the state-reset fix.
  • Restarting the daemon is a fix, not a root-cause analysis. It recreates firewall chains and reaps stale proxies, but if a host service or another project's container owns the port, the error comes straight back on the next start.
  • Do not fix this by silently disabling Docker's firewall integration (--iptables=false) or by flushing rules yourself — that removes the NAT layer that makes published ports reachable at all.

Cloudflare Error 1102 CPU Time Limit Exceeded: Fix

Your Cloudflare Worker route just started serving Error 1102, and the error page tells you almost nothing: "Worker exceeded resource limits." Error 1102 is Cloudflare's code for one of two limits — CPU time or memory — and in most real incidents it is CPU: your Worker ran synchronous JavaScript for longer than the plan allows, the runtime terminated the isolate, and the visitor got the generic error page instead of your response.

I hit this last month on a catalog search Worker. Locally the handler answered in 40 ms; at the edge the same request died with 1102 on the Free plan's 10 ms CPU budget. The confusing part was that nothing in the code looked slow — a KV read, a JSON parse, a filter loop. The fix was understanding which of those operations actually counts as CPU time, because the answer is not what most people expect.

TL;DR

  • Error 1102 = your Worker exceeded a resource limit — CPU time or the 128 MB memory ceiling. The public page says only "Worker exceeded resource limits"; the dashboard tells you which one (see below).
  • CPU time ≠ wall time. Only synchronous JavaScript execution counts — loops, JSON parsing, sorting, string building. Time spent waiting on fetch(), KV/R2 reads, or the network does not count.
  • Free plan: 10 ms of CPU per HTTP request. Paid plan: 30 s default, configurable up to 5 minutes. Memory: 128 MB per isolate on both plans.
  • Diagnose first: Workers & Pages → your Worker → Metrics → Errors → Invocation Statuses shows "Exceeded CPU Time Limits" or "Exceeded Memory"; Logpush/analytics record the outcome as exceededCpu or exceededMemory.
  • Fix in order: profile, then cut per-request CPU (cache parsed data, index once per isolate), then raise cpu_ms on Paid, then offload heavy work to Cron Triggers or Queues. Do not confuse 1102 with the deploy-time validation error 10021 ("Script startup exceeded CPU time limit") — that one means your module top-level scope is too heavy and Cloudflare rejects the upload before any traffic is served.

mmc0: error -110 whilst initialising SD card: Fix

mmc0: error -110 whilst initialising SD card means the kernel's MMC controller tried to bring the card through the SD init sequence and the card stopped answering — -110 is ETIMEDOUT in the Linux errno table. Your Raspberry Pi (or laptop card reader, or SBC) either never boots or drops the card minutes after boot, and the same line repeats in dmesg every time the controller retries. I've chased this on Pi 4s that died in a rack after months of clean uptime, and the fix is usually one of four things — a dead card, a bad contact, a weak power supply, or a controller that can't complete the handshake. This post is the diagnostic order I use, with the exact commands and the durable fix.

TL;DR

  • -110 is a timeout, not a corruption error. The controller sent the init command (ACMD41/SD_APP_OP_COND, then the status polls) and got no valid response within the kernel's deadline. The card is electrically present but not completing the handshake.
  • The card is the suspect first, the slot second, the power third. In the majority of Raspberry Pi reports the card itself is dead or counterfeit — the init handshake fails before a single block is read.
  • Reseat and reflash with Raspberry Pi Imager before you buy anything. A card that was yanked mid-write can wedge in a state where it answers some commands but never finishes init; a fresh image with verify often clears it.
  • Test the card in a PC card reader. If it initialises there, the problem is the Pi's slot, adapter, or power rail. If it fails there too, the card is done — replace it.
  • The durable fix for production Pis is USB SSD boot — it removes the weakest component (the SD card) from the boot path entirely. Details in the last section.

ssh_exchange_identification: Connection closed by host

ssh_exchange_identification: Connection closed by remote host means the TCP connection to port 22 succeeded and then the other side closed it before the SSH identification exchange completed — so the failure is on the server or in the path, not in your key or password. On current OpenSSH clients (I reproduced this on OpenSSH 10.0p2) the same error prints as kex_exchange_identification: Connection closed by remote host; the old ssh_ prefix is what you will find in most Stack Overflow threads and on older clients. Same failure, same fixes.

I chased this one on a Friday deploy when three engineers lost SSH to the same box at once, and the usual answers — "you're banned by fail2ban", "raise MaxStartups" — turned out to be half right at best. The reason this error is so confusing is that one client-side message covers five completely different server-side stories. This post is the decision tree I now use: what the error proves, which layer actually closed the connection, and the exact fix for each cause.

TL;DR

  • The error is post-handshake. ssh_exchange_identification fires only after TCP connect succeeds. A plain firewall DROP gives you Connection timed out; a REJECT gives you Connection refused. If you see this exact string, something accepted the connection and then killed it before the SSH banner exchange finished.
  • Run ssh -vvv first. debug1: Connection established. followed immediately by the error = the server or a middlebox closed it. If you see debug1: kex_exchange_identification: banner line 0: ... lines first, you are talking to something that is not sshd (an HTTP service, a proxy, a load balancer on the wrong port).
  • Check the server logs before touching sshd_config. drop connection #N ... past MaxStartups means OpenSSH's pre-auth connection limit throttled you — the classic cause under a botnet scan. Connection closed by ... [preauth] means the client side hung up.
  • fail2ban with its default action does NOT produce this error. The default blocktype is REJECT --reject-with icmp-port-unreachableConnection refused. Only a tcp-reset ban action or an inline IPS that resets an in-flight connection produces the identification error.
  • TCP wrappers are a dead cause since 2014. OpenSSH 6.7 removed libwrap support; /etc/hosts.deny advice is obsolete on every modern distro build.
  • The durable fix is to stop exposing port 22 to the internet — SSH over Tailscale/WireGuard only. That eliminates the whole class (scanner floods filling pre-auth slots, IP bans, IPS resets) instead of tuning around it.

network_mode: host Not Working on Docker WSL2: Fix

network_mode: host in docker-compose.yml either refuses to start with the exact error "host" network_mode is incompatible with port_bindings, or it starts and then quietly does nothing reachable from Windows on Docker Desktop with WSL2. Both symptoms are the same story: host networking on Docker Desktop is opt-in and only became generally available in version 4.34, and even when it is enabled it attaches to Docker's utility VM — not to your WSL2 distro and not to Windows. That mismatch is why the 22k-view Stack Overflow question "running network mode host on windows 10 with wsl2" is still unanswered with a definitive fix.

I hit this on a Windows 10 box that was pinned to Docker Desktop 4.19 by an IT policy. The compose file worked on the Linux CI runner, failed locally with the port_bindings error, and after I removed ports: it "worked" in the sense that the container started — and then nothing on the host could reach it. This post is the decision path I use now: what host mode actually does, why Docker Desktop and WSL2 break it, and the four fixes in order of how often they are the right one.

TL;DR

  • The compose error is a contradiction in terms. "host" network_mode is incompatible with port_bindings means you declared both network_mode: host and ports: — in host mode there is no port mapping, so compose rejects the pair. Remove one of them.
  • Docker Desktop only learned host networking in 4.29 (beta) and 4.34 (GA). Before that it simply was not supported on Mac/Windows — the flag was accepted and then ignored or misbehaved. Check your version before debugging anything else.
  • Even on 4.34+, host mode is opt-in: Settings → Resources → Network → Enable host networking, signed in to a Docker account. It is layer-4 (TCP/UDP) only, does not work with Enhanced Container Isolation, and Linux containers only.
  • The namespace trap: on WSL2 the engine runs in the docker-desktop utility distro, so network_mode: host shares that namespace — localhost inside the container is not your Ubuntu distro's localhost and not Windows' localhost.
  • For 95% of services, bridge + ports: is the correct fix — not host mode. Use host.docker.internal when the container must reach a service on the Windows host.
  • If you genuinely need host mode (avahi, packet capture, dynamic port ranges), run the Docker Engine natively inside your WSL2 distro instead of Docker Desktop — then host mode shares your distro's real kernel network stack.

Cloudflare Error 1101 Worker Threw Exception: Fix

Your Cloudflare Worker route just started serving Error 1101: Worker threw exception, and the error page gives you a Ray ID but no stack trace. Error 1101 is Cloudflare's code for one thing: your Worker hit a runtime JavaScript exception it never handled, the request died, and the edge served the generic error page instead of your response. The exception itself is never shown to the visitor — it is sitting in Workers Logs, and the fastest path to a fix is knowing which of the four classic causes you are looking at.

I hit 1101 twice this month on the same API: once from a request.json() call on an empty body, once after "refactoring" ctx.waitUntil into a destructured variable. Both took longer than they should have because the error page hides the actual exception. This post is the diagnostic path I now run first: what 1101 means, the exact causes that produce it, and the production-grade handler pattern that keeps it from taking the whole route down.

TL;DR

  • Error 1101 = unhandled JavaScript exception in your Worker. The runtime caught an error, killed the request, and rendered the error page. The real exception is in Workers Logs, not on the page.
  • Four causes cover almost every real 1101: an uncaught exception in the fetch handler; "The script will never generate a response" (an unresolved promise or a WebSocket that is never closed); "Illegal invocation" (a lost this from destructuring ctx); and caching I/O objects in global scope.
  • Debug in order: Workers & Pages → your Worker → Logs, filter $metadata.error EXISTS, or npx wrangler tail for live exceptions. The stack trace names the cause in one line.
  • Harden the handler: wrap your logic in try/catch and return a structured error response with the Ray ID; for proxy Workers, ctx.passThroughOnException() sends unhandled errors to the origin instead of the 1101 page.
  • Not every 11xx is your code. 1102 is CPU time, 1027 is the free-tier daily request limit, and other 11xx errors can mean a runtime incident — check the Cloudflare status page before rewriting code.

Tailscale SSL_ERROR_RX_RECORD_TOO_LONG: Fix HTTPS

Firefox throws SSL_ERROR_RX_RECORD_TOO_LONG on a https://machine.tailnet.ts.net URL when something on the machine's port 443 answers with plain HTTP instead of TLS. I hit this twice in the same week — once on a NAS after running tailscale cert, once after uninstalling a reverse proxy that had been squatting on 443 — and both times the fix was about who owns the port, not about the certificate files themselves.

This is a Firefox-specific rendering of a generic failure: a TLS client sent a ClientHello and got back bytes that are not TLS. Chrome shows ERR_SSL_PROTOCOL_ERROR for the same condition, and curl reports wrong version number — I reproduced all three against a local plain-HTTP server on port 443 before writing this. Here is the diagnostic order that finds the culprit, and the exact fixes for the three Tailscale setups that produce this error.

TL;DR

  • SSL_ERROR_RX_RECORD_TOO_LONG = the port answered, but not with TLS. The server on 443 spoke plain HTTP to a TLS ClientHello. A closed port gives "connection refused", not this error.
  • On Tailscale, the cause is one of three things: HTTPS Certificates not enabled for the tailnet, tailscale cert output never installed into the actual service, or another process (leftover reverse proxy, container) squatting on port 443.
  • Diagnose in two commands: curl http://host:443/ (200 = plain HTTP on 443, 400 = TLS port correctly rejecting HTTP) and sudo ss -tlnp | grep :443 to see who owns the port.
  • Fix the root cause — enable HTTPS in the admin console, point tailscale serve at the app, or remove the squatter. Do not just click past the warning.

Klipper "MCU 'mcu' shutdown: ADC out of range" Fix

MCU 'mcu' shutdown: ADC out of range is the Klipper shutdown you get when a heater's temperature sensor reads outside its configured range — the micro-controller's analog-to-digital converter reported a sample the config says cannot happen. It is the most common Klipper failure on machines with a loose or dead thermistor, and the console message that follows usually names the exact sensor and the range it violated.

I chased this one on a printer that printed fine for a year, then started shutting down mid-print on the second layer. The heater was fine, the firmware was fine — the thermistor connector had worked itself loose. This post walks the exact message, the three causes I've seen in the field, and the check order that finds the fault in minutes instead of evenings.

TL;DR

  • The message is a safety shutdown, not a config error. The MCU's ADC read a temperature outside the sensor's configured min_temp/max_temp, so Klipper stopped everything.
  • Cause #1 by far: the thermistor. Disconnected, loose, broken, or end-of-life — it reads as an open circuit, which lands outside the valid range.
  • Cause #2: the wrong sensor type in printer.cfg. A mismatched sensor_type (wrong NTC B-value, wrong beta table) makes a healthy thermistor read nonsense.
  • Fix order: read the clarify line, check the reported temperature, reseat and inspect the sensor, audit sensor_type + min_temp/max_temp, then FIRMWARE_RESTART.

Nmap "requires root privileges" Error: Fix SYN Scan

nmap prints You requested a scan type which requires root privileges. and exits with QUITTING! whenever you ask for a raw-packet scan — SYN, UDP, ACK, OS detection, idle — as a non-root user. The fix is not to hand everyone sudo; it is to run the right scan type at the right privilege level, and to know which of nmap's scan classes actually need root at all.

I hit this exact wall on an assessment box where the client's policy forbade passwordless sudo for the scanning account. nmap 7.95 on Debian (trixie) printed the message below the moment I typed -sS. Everything in this post is what that box actually printed, or comes from the official documentation — you can reproduce it on your own machine in under a minute.

TL;DR

  • The error is not a bug. nmap refuses raw-packet scans without root because forging TCP packets requires CAP_NET_RAW, which your user does not have.
  • SYN scan (-sS) is the default and the most common trigger. Switching to the connect scan (-sT) removes the root requirement entirely and gives you the same open/closed answer for ordinary port discovery.
  • Root-only scan types (SYN, UDP, OS detection) run through interactive sudo; the unprivileged path — -sT — is the one that survives audits and containerized scanners.

Nginx 499 Client Closed Request: Why It Happens, How to Fix

An access-log line that ends in 499 0 means the client closed the connection before nginx finished answering — the 0 is the number of bytes nginx managed to send, which is zero. The 499 status is nginx's private "client closed request" code: it is not an HTTP response, it never reaches the browser, and it is far too easy to read as an nginx fault when the real problem sits upstream.

I hit a wall of 499s on a reverse proxy in front of a Python API, spent an evening assuming nginx was broken, and finally reproduced the exact behavior on a lab nginx 1.30.4 with a deliberately slow upstream. Everything in this post — the log lines, the status codes, the timeouts — is what that lab actually printed. Here is what 499 means, who really closed the connection, and how to find the component at fault.

TL;DR

  • 499 = the client closed the connection while nginx was still waiting on the upstream (or mid-response). It exists only in nginx's logs; it is never sent over the wire.
  • It is a symptom, not the fault. Someone gave up before nginx could answer — usually an upstream that answered too slowly, or a proxy, load balancer, or health check that timed out first.
  • Fix the timeout chain (CDN/LB timeout ≥ nginx timeout ≥ app timeout), move long work out of the request path, and log $upstream_response_time so the next 499 tells you exactly who was slow.

C2CZ

Hyper-specific engineering guides: Cloudflare Workers, Turso, self-hosting, network security, offensive security, independent operations.