Docker · Lesson 0003

Inspect config, exec a shell

When logs alone are not enough: read the container’s full config as JSON, then run a one-off command (or a shell) inside the running process isolation — without Docker Desktop.

Mission link: Production debugging is CLI-first. inspect answers “what was this container configured as?” exec answers “what does the filesystem / process look like from the inside?” Both skills transfer when AI-generated stacks misbehave.

Continues lesson 0002 · cheat sheets pull/tags/logs · first-run CLI

The debugging ladder so far

  1. docker ps / docker ps -a — is it running or exited?
  2. docker logs — what did the main process print?
  3. This lesson: docker inspect — full config and network facts.
  4. This lesson: docker exec — run a command inside the live container.

logs = the flight recorder · inspect = the blueprint of the plane · exec = climbing into the cabin while it’s flying. Different questions, same “why is this broken?” mission.

Two tools, two jobs

docker inspect

Print low-level JSON about a Docker object (container, image, …). No process starts. You read config: image used, env, ports, state, network addresses. (inspect reference)

docker exec

Run a new command in a running container. Only works while PID 1 is up; the exec process is not restarted if the container restarts. (exec reference)

docker run -d … --name lesson3 nginx:alpine │ ├─ docker logs lesson3 ← stdout / stderr of main process │ ├─ docker inspect lesson3 ← JSON config (ports, image, env, …) │ └─ -f '{{…}}' ← pluck one field │ └─ docker exec lesson3 ls / ← one-shot command inside docker exec -it … sh ← interactive shell (-i + -t)

inspect: read the blueprint

Default output is a JSON array — useful, noisy. In practice you almost always pass -f / --format with a Go template to print one fact. Official docs show patterns for IP, image name, ports, and subsections as JSON. (format option)

# Full JSON (pipe to less if long)
docker inspect lesson3

# Which image is this container actually running?
docker inspect -f '{{.Config.Image}}' lesson3

# Container IP on the bridge network (when present)
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' lesson3

# Pretty-print just the Config object as JSON
docker inspect -f '{{json .Config}}' lesson3

Template examples from the official inspect docs (image name, network IP, json helper).

exec: work from the inside

docker exec is not “attach to the main process.” It starts a separate command in the same container namespaces. The command must be an executable that exists in the image; chain with sh -c "…" when you need shell features. (exec description)

Must be running

exec fails if the container is stopped or paused. Check docker ps first. Inspect still works on stopped containers (you can read why it exited from state fields) — another reason both tools matter.

Check: inspect vs exec vs logs

You need the image name and port maps as data. Which tool?

Why do people write docker exec -it name sh?

Skill: run → inspect → exec → clean

Do this in your terminal. Engine must be running. Reuse nginx:alpine from lesson 0002 if you still have it locally (docker image ls).

  1. Start a named container
    docker run -d -p 8080:80 --name lesson3 nginx:alpine
    If the name is taken: docker rm -f lesson3 first. If 8080 is busy: -p 18080:80 instead.
  2. Confirm running, then inspect (full JSON)
    docker ps
    docker inspect lesson3 | less
    Scroll: look for Config, NetworkSettings, State. Quit less with q. (inspect defaults to a JSON array.)
  3. Format: image name only
    docker inspect -f '{{.Config.Image}}' lesson3
    Expect something like nginx:alpine. This is the official “get instance’s image name” pattern.
  4. Format: container IP (if any)
    docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' lesson3
    On Docker Desktop you often still use published ports (localhost:8080) rather than this IP from the Mac host — but the field is real and useful when containers talk to each other on a user-defined network (later).
  5. One-shot exec: list the web root
    docker exec lesson3 ls -la /usr/share/nginx/html
    You should see index.html (and maybe other static files). No -it needed for a single non-interactive command.
  6. Interactive shell, then leave
    docker exec -it lesson3 sh
    # inside the container:
    pwd
    hostname
    cat /usr/share/nginx/html/index.html | head
    exit
    Official pattern: docker exec -it … sh. After exit, docker ps still shows lesson3 — you only left the exec shell, not the main process. (exec examples)
  7. Prove exec does not replace the main process
    open http://localhost:8080
    docker logs --tail 5 lesson3
    The welcome page still works; access lines may appear in logs. Exec was a side command, not a takeover of nginx.
  8. Clean up
    docker stop lesson3
    docker rm lesson3
    Image nginx:alpine remains for next time.
Security note (preview)

exec as root inside a container is powerful — and common on default images. Later, when you ship TypeScript apps, you’ll prefer non-root users and treat shell access as a break-glass debug tool, not “how we deploy changes.” For now: learn the tool; don’t leave shells open on shared machines.

Check: what stays running?

You exit an exec -it … sh session. What happens?

Container is stopped. Which command still works?

Why this matters for shipping

Primary source (read this next)

docker container exec — Docker Docs (official CLI reference)

Read the description (running container only; not restarted with the container) and the -it shell example. Keep docker inspect open for the --format recipes (image, IP, ports, json).

Quick reference (commands from this lesson)

docker run -d -p 8080:80 --name lesson3 nginx:alpine
docker inspect lesson3
docker inspect -f '{{.Config.Image}}' lesson3
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' lesson3
docker exec lesson3 ls -la /usr/share/nginx/html
docker exec -it lesson3 sh
docker stop lesson3 && docker rm lesson3