For debugging and testing network and API connectivity, ~30 MB Alpine Linux "Swiss Army knife".
1.1K
A ~30 MB Alpine "Swiss Army knife" for debugging network and API connectivity from inside a container network.
Exec into it from inside the network you are debugging, when something will not talk to something else.
docker pull thenetworkfactory/tiny-tools
Ad-hoc, against a stack network — this is the common case, because the whole point is to be inside the network you are debugging:
docker run --rm -it --network myproject_default \
thenetworkfactory/tiny-tools bash
Against the host's network stack instead of a container one:
docker run --rm -it --network host thenetworkfactory/tiny-tools bash
As a long-lived service in a compose stack — give it no ports and hold
it open with sleep infinity so exec works:
docker compose exec tiny-tools bash
Why the sleep: the image's CMD is an interactive shell, which
exits immediately under compose (no TTY) and would leave the container
restart-looping.
fish is installed too, if you prefer it: docker run -it … fish.
# Does the name resolve at all, and to what?
dig +short api.example.internal
host api.example.internal
# Which resolver is the container actually using?
cat /etc/resolv.conf
# Ask a specific resolver — is it the resolver that is broken, or the record?
dig @8.8.8.8 example.com
dig @127.0.0.11 api # Docker's embedded DNS, inside a compose network
# Full chain, including CNAMEs and the authoritative answer
dig +trace example.com
# Is this a DNS problem or a connectivity problem? drill is terser than dig.
drill -V0 example.com
# Reverse lookup
dig -x 10.42.0.7
Inside a compose network, service names resolve via Docker's embedded
DNS at
127.0.0.11. If dig api works but curl http://api does not, the
problem is not DNS.
# Fastest yes/no on a single port
nc -zv api 8000
# Range, on one host
nmap -p 1-1000 api
# What is listening in THIS container / on this host (--network host)
ss -tulnp
# Which process holds a port open
lsof -i :8000
nmap's scripting engine (NSE) is not included — Alpine splits it
into nmap-scripting. apk add --no-cache nmap-scripting if needed.
# Status, timings and the redirect chain, no body
curl -sS -o /dev/null -w 'code=%{http_code} time=%{time_total}s\n' https://example.com
curl -sSIL https://example.com
# Bypass DNS and hit a specific backend, keeping the Host header —
# the single most useful trick for debugging reverse-proxy routing
curl -sS -H 'Host: api.example.internal' http://<container-ip>:8000/
# Same, without needing the IP by hand
curl -sS --resolve api.example.internal:80:172.18.0.5 \
http://api.example.internal/
# What security headers is the proxy actually adding?
curl -sSI https://example.com | grep -iE 'content-security|strict-transport|referrer|x-frame'
# Is the proxy serving the app, or a maintenance/error page?
curl -sS https://example.com | head -20
# Pretty-print a JSON API
curl -sS http://api.example.internal/health | jq .
# A reverse proxy's own admin API — which route matched?
# (Traefik shown; adjust the path for nginx/Caddy/HAProxy.)
curl -sS http://proxy-admin.example.internal:8080/api/http/routers | jq -r '.[].name'
# Full handshake, chain and expiry
openssl s_client -connect example.com:443 -servername example.com </dev/null
# Just the dates
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -dates -subject -issuer
# Does it negotiate TLS 1.3?
openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>&1 | grep -E 'Protocol|Cipher'
# Is the chain complete, or is an intermediate missing?
openssl s_client -connect example.com:443 -showcerts </dev/null 2>/dev/null | grep -c 'BEGIN CERT'
ip -brief addr # what IPs does this container have
ip route # default gateway
ip neigh # ARP cache
traceroute api # where does it stop
mtr -rwc 20 8.8.8.8 # sustained path + loss (report mode)
ethtool eth0 # link speed / duplex (--network host)
ip and ss are the modern replacements for ifconfig and netstat;
net-tools is installed too, so both work.
Needs NET_ADMIN + NET_RAW — see the note below.
# Everything to/from a host, no name resolution (-n keeps it fast)
tcpdump -ni any host api
# Just the HTTP port, with payload
tcpdump -nAs0 -i any port 8000
# Write a pcap out to the host for Wireshark
tcpdump -ni any -w /tmp/cap.pcap port 5672
# Between two containers: server on one, client on the other
iperf3 -s # on A
iperf3 -c A -t 10 # on B
# Quick HTTP load check
ab -n 200 -c 10 https://example.com/
# Generate a bcrypt htpasswd entry (basic-auth middleware)
htpasswd -nbB admin 'somepassword'
sipcalc 10.42.0.0/22 # range, broadcast, usable addresses
sipcalc -s 26 10.42.0.0/22 # split into /26s
procps is installed, so these are the real tools rather than the
busybox applets:
ps aux
top
free -m
vmstat 1 5
conntrack -L # live NAT + connection table
conntrack -E # follow events as they happen
# Postgres / MySQL / Redis clients are NOT installed (too large) — but
# you can still prove the port answers and speaks the right protocol:
nc -zv db 5432
(printf 'PING\r\n'; sleep 1) | nc redis 6379 # expect +PONG
# Forward a container-internal port to your shell for a one-off
socat TCP-LISTEN:15432,fork TCP:db:5432
rsync -av /data/ user@host:/backup/
tcpdump and mtr need NET_ADMIN + NET_RAW. These are not
granted by default — a debug shell should not carry capabilities it
does not need.
Without them tcpdump does not fail outright; it warns
(That device doesn't support promiscuous mode) and captures only
what the container itself sends or receives. If a capture looks
suspiciously empty, check the capabilities before chasing the network.
# Ad-hoc
docker run --rm -it --cap-add NET_ADMIN --cap-add NET_RAW \
--network myproject_default thenetworkfactory/tiny-tools bash
In compose, add the same via cap_add: [NET_ADMIN, NET_RAW].
| Area | Tools |
|---|---|
| Shells | bash, fish, coreutils, nano |
| DNS | dig, host, nslookup (bind-tools), drill |
| HTTP / transfer | curl, rsync |
| Sockets | nc (openbsd), socat, openssl |
| Reachability | ping, traceroute, mtr |
| Interfaces / routing | ip, ss (iproute2), ifconfig, netstat, route (net-tools), ethtool |
| Firewall / NAT | conntrack |
| Scanning / capture | nmap, tcpdump |
| Throughput / load | iperf3, ab, htpasswd |
| Process / host | ps, top, free, vmstat (procps), lsof |
| Data / files | jq, tree |
| Addressing | sipcalc |
| SSH / git | ssh, ssh-keygen, sshpass, git |
The image is meant to stay small enough to pull without thinking about it. These were measured and rejected — each would dominate a ~30 MB image:
| Package | Installed size |
|---|---|
httpie | 70 MB |
tshark | 65 MB |
rclone | 56 MB |
nodejs | 39 MB |
python3 | 29 MB |
mariadb-client | 17 MB |
yq-go | 8 MB |
bat | 5 MB |
postgresql-client | 3.4 MB |
fd | 3.2 MB |
redis (CLI) | 2.7 MB |
ripgrep | 2.6 MB |
strace | 1.9 MB |
Any of them can be added for the life of a container:
apk add --no-cache postgresql-client
Maintained by AxisOps.
Content type
Image
Digest
sha256:91bb11a23…
Size
31.3 MB
Last updated
about 1 month ago
docker pull thenetworkfactory/tiny-tools