Files
command-center/agent/managed-skills/podman-netns-addressability-probe/SKILL.md
T
Frank 9bfa0be85d chore(skills): promote runbook skills to managed-skills; drop superseded SKILL.md
- 31 new managed skills (anubis/arr/authentik/hermes/freetoken/podman/llm-swap
  runbooks etc.) now tracked.
- Removed superseded skills: omp_harness_config, peon_ping_integration
  (replaced by user-authored peon-ping-* managed skills),
  rocmfp4-strix-benchmark-orchestration.
- New memory-root skills: niri_compositor_ops, omp_git_workflow,
  omp_model_config, quadlet-media-migration, homelab-service-triage,
  hermes-dankclaw-ops, frank-default-large-ops, caddy-auth-dns-playbook.
2026-09-09 16:20:28 -07:00

2.8 KiB

name, description
name description
podman-netns-addressability-probe Runbook to answer "can container X actually reach service Y" on a rootless-podman/netavark host, instead of assuming localhost works everywhere; includes the probe commands and the false-positive traps.

Podman netns addressability probe

Answer "can this container actually dial that service?" per network namespace, not per host. On rootless podman + netavark, a bridge-net container cannot reach host-netns loopback listeners at all, so localhost:PORT reasoning is wrong for it.

Decision rule

  • NetworkMode=host → the container is the host netns: dials everything on 127.0.0.1, binds any free port.
  • bridge/named network → own netns. Only published ports are reachable, via the network's gateway IP. Unpublished host listeners are refused instantly (RST), not routed — even when the listener is bound 0.0.0.0.

Probe sequence (all read-only, run on the host)

  1. Namespace: podman inspect <name> --format '{{.HostConfig.NetworkMode}}'
  2. Owner of a listener: sudo ss -ltnp 'sport = :<port>' (host netns only — this is why a 0.0.0.0 listener can still be unreachable from a bridge net).
  3. From inside the suspect container: podman exec <name> cat /proc/net/route → default route = gateway IP to dial.
  4. Probe the candidate address inside that netns: podman exec <name> sh -c 'timeout 2 bash -c "exec 3<>/dev/tcp/<gw-ip>/<port>"; echo rc=$?'

Interpretation (do not blur these)

  • instant refusal = nothing listening in a reachable namespace (or rootlessport rejected an unpublished port map).
  • timeout = no route to that address at all. host.containers.internal (169.254.1.2) is an alias on the default podman net only; containers on a named network time out against it rather than being refused.

Probe-tool gotchas verified on this host (podman 5.x, netavark)

  • podman network inspect <net> --format '{{json .containers}}' errors (can't evaluate field containers in type interface {}) — membership is not exposed there. List from the container side instead: podman ps -a --format '{{.Names}} {{.Networks}}'
  • An empty Networks field in that output = host network or a shared namespace (--network=container:<other>), not "no network".
  • Quadlet Network=<name>.network yields runtime network name systemd-<name>; probe with podman network ls, never by the .network filename.

Fix options when a bridge-net container must reach a host-netns listener

(a) move it to Network=host, or (b) publish the port — (b) makes that port reachable from every namespace and the LAN, so it is an exposure change needing explicit operator approval, never a silent fix.

Repo source of truth for this fleet: serverAppConfigs-docs/Architecture/Container Network Namespaces.md (commit 2e179b0).