Containerization with Docker

Chapter 3
Container Lifecycle and Docker CLI

Diploma in DevOps
Containerization with Docker   •   Diploma

Container Lifecycle and Docker CLI

Introduction

Docker containers provide isolated, reproducible environments for running applications and their dependencies. A container is not a lightweight virtual machine; it is a process, or group of processes, isolated by the operating system through mechanisms such as namespaces and control groups. The Docker CLI supplies the practical interface through which developers and operators create, start, inspect, operate, monitor, stop, restart, and remove containers.

Understanding the container lifecycle is essential in DevOps because an application can be correctly packaged yet still fail through an incorrect command, missing environment variable, unavailable port, insufficient memory, or unsuitable restart policy. Lifecycle knowledge also supports reliable deployment, incident response, security, and automation.

Learning Objectives

By the end of this chapter, you should be able to:

  • distinguish images, containers, processes, and lifecycle states;
  • create and run containers using the Docker CLI;
  • publish ports and supply environment variables safely;
  • inspect container configuration, processes, logs, and resource use;
  • execute commands inside running containers;
  • stop, restart, pause, kill, and remove containers appropriately;
  • apply CPU, memory, and restart controls;
  • troubleshoot common lifecycle and networking failures; and
  • select professional practices for reliable and secure container operations.

1. Images, Containers, and the Docker Engine

A Docker image is an immutable, layered package containing a filesystem and metadata. It normally includes an application, runtime libraries, configuration defaults, and an instruction identifying the process to start. A container is a runtime instance of an image. Multiple containers may be created from the same image, with each container having its own writable layer, process, network identity, and lifecycle state.

The Docker Engine consists conceptually of a client, a daemon, and an execution environment. The Docker CLI is the client. It sends requests to the Docker daemon, which manages images, containers, networks, and volumes. The daemon asks a container runtime to create the isolated process.

A useful distinction is:

ObjectMain purpose
ImageImmutable application template
ContainerRuntime instance of an image
VolumePersistent data managed independently of a container
NetworkCommunication mechanism between containers and external clients
RegistryRepository for storing and distributing images

The command docker run is a high-level operation. It can create a container from an image and start it in one command. In contrast, docker create creates the container but does not start its process, while docker start starts an existing stopped container.

2. Lifecycle States and Transitions

A container normally moves through the following states:

  • Created: configuration exists, but the main process has not started.
  • Running: the configured main process is executing.
  • Paused: processes are temporarily frozen.
  • Exited: the main process has terminated, either normally or because of an error.
  • Restarting: Docker is attempting to start the container again under a restart policy.
  • Dead: Docker could not completely remove or manage the container.

The most important rule is that a container’s lifetime is tied to its main process, also called PID 1 within the container. If that process exits, the container normally becomes exited. A container does not remain alive merely because its image exists.

Docker container lifecycle state diagram Created Running Exited Paused start exit / stop pause unpause restart / start
Docker container lifecycle state diagram
Figure: Docker container lifecycle state diagram

Figure: Principal Docker container lifecycle states and transitions.

The command docker ps lists running containers. docker ps -a lists running and stopped containers. A container’s status should be interpreted together with its exit code and logs. For example, exit code 0 commonly indicates normal termination, whereas a non-zero code indicates an error; however, the meaning depends on the application.

3. Creating and Running Containers

The general form of the most frequently used command is:

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

For example:

docker run --name web-test -d -p 8080:80 nginx:latest

This command:

  1. downloads nginx:latest if it is not available locally;
  2. creates a container named web-test;
  3. maps host TCP port 8080 to container TCP port 80;
  4. starts the container in detached mode; and
  5. returns the container identifier.

Useful options include:

OptionPurposeExample
--nameAssign a readable name--name api
-dRun in background-d
-itInteractive terminal-it
--rmRemove container after exit--rm
-pPublish a port-p 8080:80
-PPublish exposed ports automatically-P
--env or -eSet an environment variable-e MODE=prod
--env-fileRead variables from a file--env-file .env
--networkAttach to a network--network app-net
--volume or -vMount storage-v data:/var/lib/app
--restartDefine restart behavior--restart unless-stopped
--memorySet memory limit--memory 512m
--cpusSet CPU quota--cpus 1.5

Foreground and interactive execution

A foreground container attaches its standard input, output, and error streams to the terminal. This is useful for observing a short-lived command:

docker run --rm alpine:3.20 echo "container completed"

An interactive shell can be started with:

docker run --rm -it alpine:3.20 sh

The -i option keeps standard input open, and -t allocates a pseudo-terminal. They are commonly combined as -it; neither option makes a service persistent by itself.

Create, then start

Separating creation from starting is useful when configuration must be reviewed:

docker create --name database -e POSTGRES_PASSWORD=change-me postgres:16
docker inspect database
docker start database

This separation is also valuable in automation because it makes configuration and execution distinct operations.

4. Ports and Container Networking

A process inside a container may listen on a container port, but that port is not automatically reachable from the host. Port publishing creates a host-to-container forwarding rule:

docker run -d --name web -p 127.0.0.1:8080:80 nginx

The syntax is generally [HOST_IP:]HOST_PORT:CONTAINER_PORT[/PROTOCOL]. Thus, the example binds host loopback address 127.0.0.1, host port 8080, and container TCP port 80. Binding to 127.0.0.1 prevents direct access through external host interfaces. Binding to 0.0.0.0 exposes the port on all host interfaces and should be used only when required and protected by suitable network controls.

Common port failures include:

  • the application listens on 127.0.0.1 inside the container rather than 0.0.0.0;
  • the host port is already occupied;
  • the wrong container port was published;
  • a firewall or cloud security group blocks traffic; or
  • the service is not ready even though the container is running.

Useful commands include:

docker port web
docker inspect web
docker network ls
docker network inspect bridge

Containers on the same user-defined network can normally reach each other by container name. This is preferable to hard-coding changing IP addresses. For example:

docker network create app-net
docker run -d --name redis --network app-net redis:7
docker run -d --name api --network app-net -e CACHE_HOST=redis my-api:1.0

5. Environment Variables and Configuration

Environment variables provide runtime configuration without rebuilding an image:

docker run -d --name api \
  -e APP_MODE=production \
  -e DATABASE_HOST=db \
  -e DATABASE_PORT=5432 \
  my-api:1.0

A file may be used for several variables:

docker run --env-file production.env my-api:1.0

Variables should be treated as configuration inputs, not as a complete secrets-management system. Environment variables can be exposed through inspection, process diagnostics, logs, crash reports, or orchestration interfaces. Passwords, API tokens, and private keys should preferably be supplied through a dedicated secret-management mechanism, with restricted file permissions and rotation procedures.

The image may define defaults using ENV, but a value supplied at runtime generally overrides that default. Configuration precedence should be documented because application defaults, image settings, command-line values, mounted files, and orchestration settings can interact.

6. Inspecting and Monitoring Containers

Inspection should proceed from general status to specific evidence.

docker ps -a
docker inspect api
docker logs api
docker logs --tail 100 --timestamps api
docker top api
docker stats api

docker inspect returns structured JSON containing identifiers, state, mounts, network settings, environment variables, image information, and configuration. Important fields include State.Status, State.ExitCode, State.Error, State.StartedAt, and State.FinishedAt.

To extract a particular field:

docker inspect --format '{{.State.Status}}' api
docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' api

docker logs displays data written to the container’s standard output and standard error. It does not automatically display messages written to arbitrary files inside the container. Production applications should normally log structured, useful events to standard streams so that Docker or a logging platform can collect them.

docker stats reports live resource usage. It is an operational view, not a replacement for application monitoring. A container may use little CPU while returning errors, and a healthy resource graph does not prove that a service is available.

7. Executing Commands in a Running Container

The docker exec command starts an additional process in an existing running container:

docker exec -it api sh
docker exec api printenv APP_MODE
docker exec api ls -la /app

exec is useful for diagnosis, checking configuration, and controlled administrative tasks. It does not change the image, and changes made to the writable layer are normally lost when the container is deleted. It is therefore not a substitute for changing the Dockerfile or deployment configuration.

A common mistake is attempting to execute a shell that is not present. Minimal images may contain sh but not bash; some contain no shell at all. In such cases, use an available diagnostic executable, a temporary debugging image, or application-specific health and diagnostic endpoints.

8. Stopping, Restarting, Pausing, and Removing

The principal lifecycle commands are:

docker stop api
docker start api
docker restart api
docker pause api
docker unpause api
docker kill api
docker rm api

docker stop asks the main process to terminate gracefully and waits for a configurable period before forcing termination. docker kill sends a signal immediately by default and is appropriate when a process is unresponsive or urgent termination is required. Graceful shutdown is preferable because applications may need to flush data, close connections, or release locks.

A stopped container can be started again with its original configuration. docker restart stops and starts it. docker pause freezes processes without terminating them; it is not a normal application shutdown mechanism.

A running container cannot normally be removed:

docker rm api
 docker rm -f api

The -f option forcibly stops and removes the container and should be used deliberately. Removing a container does not normally remove named volumes, images, or other containers. The --rm option is convenient for temporary jobs because Docker removes the container automatically after it exits.

9. Resource Limits and Restart Policies

Without controls, a container can consume excessive host resources. Resource limits create operational boundaries:

docker run -d --name worker \
  --memory=512m \
  --memory-swap=512m \
  --cpus=1.0 \
  worker:1.0

Here, memory is limited to 512 MiB and CPU capacity is limited approximately to one CPU. Actual scheduling depends on host capacity and competing workloads. Memory limits can cause an out-of-memory termination; CPU limits generally throttle rather than terminate a process.

Resource controls should be selected from measured workload behavior. A limit that is too low causes instability; one that is too high weakens isolation. Monitor working-set usage, throttling, restart frequency, latency, and application-specific indicators.

Restart policies include:

--restart=no
--restart=on-failure:5
--restart=always
--restart=unless-stopped

on-failure is appropriate for retrying failed jobs, while always and unless-stopped are commonly used for long-running services. Restart policies do not repair an application defect; they may create a crash loop. A health check and alerting are therefore important.

10. Troubleshooting Method

A disciplined procedure prevents random command execution.

Step 1: Identify the actual object

docker ps -a --no-trunc

Confirm the name, image, status, and exit code. Names may be confused with image tags.

Step 2: Read logs and metadata

docker logs --tail 200 api
docker inspect api

Look for missing configuration, binding errors, permission failures, migrations, and dependency failures.

Step 3: Verify the process and network

docker top api
docker port api
docker inspect --format '{{json .NetworkSettings.Networks}}' api

Check whether the process is listening on the intended interface and whether the published port matches the service port.

Step 4: Test from the correct location

A service may be reachable from another container but not from the host, or reachable locally but blocked externally. Test each path separately. Use a temporary diagnostic container on the same network when appropriate.

Step 5: Check resources and dependencies

docker stats api
docker events --since 10m

Inspect memory pressure, CPU throttling, restarts, and dependency availability.

Step 6: Reproduce with a controlled command

Run the image interactively or override its command when safe:

docker run --rm -it --entrypoint sh my-api:1.0

Do not modify production containers casually. Record changes and reproduce the fix in source configuration, the Dockerfile, or the deployment definition.

SymptomLikely causesFirst checks
Container exits immediatelyMain process completed, bad command, missing configdocker logs, exit code, image command
Port unavailableWrong mapping, host conflict, service not listeningdocker port, logs, listener configuration
Cannot connect to another containerDifferent network, wrong name, dependency not readydocker network inspect, DNS/name, readiness
Repeated restartsCrash, failed health logic, restart policydocker inspect, logs, events
Permission deniedUser identity, ownership, read-only mountdocker exec, mount options, file ownership
Out-of-memory terminationLimit too low or leakdocker stats, host memory, exit state

11. Applied Case: A Failing API Container

The following is an illustrative case. A small development team deploys an API using:

docker run -d --name orders-api -p 8080:8080 orders-api:2.1

Users report that the service is unavailable. The team first runs docker ps, discovers that the container is not listed, and then runs docker ps -a. The status is Exited (1).

Analysis

The team examines the logs:

docker logs orders-api

The application reports that DATABASE_HOST is missing. This establishes that the container process started but terminated during configuration validation. The team then inspects the image and confirms that the API expects a database hostname and credentials at runtime.

A second test supplies configuration and connects both services to a user-defined network:

docker network create orders-net
docker run -d --name orders-db --network orders-net \
  -e POSTGRES_PASSWORD=temporary-value postgres:16
docker run -d --name orders-api --network orders-net \
  -p 127.0.0.1:8080:8080 \
  -e DATABASE_HOST=orders-db \
  -e DATABASE_PORT=5432 \
  orders-api:2.1

The team checks logs, port publication, and resource use. They also discover that the API starts before the database is ready. They add application retry logic and a health/readiness design rather than relying solely on container start order.

Outcome and learning points

The immediate failure was caused by missing configuration, while the second potential failure involved readiness rather than networking. The case demonstrates that “container running” does not mean “application ready.” It also demonstrates the importance of container names on user-defined networks, localhost-only port binding during development, explicit configuration, and evidence-based troubleshooting.

12. Security, Governance, and Professional Practice

Operational convenience must not override security. Apply least privilege by running as a non-root user when possible, limiting capabilities, avoiding privileged mode, and mounting only necessary paths. Do not place secrets in images, Dockerfiles, shell history, public command output, or source repositories.

Use trusted, versioned image tags or digests for reproducibility. The tag latest can point to different image content over time, so it is unsuitable as a strong production version identifier. Scan images for vulnerabilities, remove unnecessary packages, patch base images, and maintain an inventory of deployed versions.

Container isolation is not an absolute security boundary. The Docker daemon and host kernel are highly privileged components. Access to the Docker socket can effectively provide host-level control, so socket exposure must be tightly controlled. Logging should avoid sensitive values, and retained logs should follow organizational privacy and retention policies.

13. Contemporary Practice

The Docker CLI is widely used for local development and single-host operations. Larger environments commonly use Compose, Kubernetes, or another orchestrator. These systems add declarative configuration, service discovery, health management, rolling updates, and secret integration, but they do not eliminate the need to understand individual container lifecycle behavior.

Modern build and deployment pipelines commonly combine image scanning, software bill-of-materials generation, provenance or signing, automated tests, and controlled promotion between environments. AI-assisted tools can help summarize logs or suggest commands, but operators must verify proposed actions, particularly commands that remove containers, expose ports, alter permissions, or reveal sensitive data.

Best Practices Checklist

  • Use explicit image versions and document the expected architecture.
  • Give important containers meaningful names.
  • Keep application state in volumes or external services, not ephemeral writable layers.
  • Publish only required ports and bind development services narrowly.
  • Use user-defined networks and service names for inter-container communication.
  • Supply configuration through controlled mechanisms and protect secrets.
  • Emit useful logs to standard output and error.
  • Define resource limits and observe actual usage.
  • Use graceful shutdown and an appropriate restart policy.
  • Add health and readiness checks for services with dependencies.
  • Treat exec changes as temporary and reproduce permanent changes declaratively.
  • Remove unused containers and images through controlled housekeeping.
  • Record operational actions in production environments.

Chapter Summary

Docker containers are runtime instances of immutable images, and their behavior is governed by the lifecycle of the main process. The Docker CLI supports creation, execution, inspection, diagnosis, stopping, restarting, pausing, and removal. Port publishing controls host access, while user-defined networks support name-based communication between services. Environment variables provide runtime configuration but require careful handling when they contain secrets. Inspection, logs, process views, events, and resource statistics provide complementary evidence during troubleshooting. Memory and CPU limits improve isolation, while restart policies must be combined with monitoring and application health logic. Professional container operations require reproducibility, least privilege, controlled networking, secure configuration, explicit versions, and declarative correction of problems.

Key Terms

Container: An isolated runtime instance of an image.

Image: Immutable, layered package used to create containers.

Docker daemon: Background service that manages Docker objects and operations.

Docker CLI: Command-line client used to interact with Docker.

PID 1: The main process whose termination normally ends a container.

Created: Lifecycle state in which configuration exists but execution has not started.

Running: State in which the main container process is executing.

Exited: State in which the main process has terminated.

Port publishing: Mapping of a host port to a container port.

Environment variable: Runtime key-value configuration supplied to a process.

User-defined network: Docker network providing controlled container communication and name resolution.

Volume: Persistent storage managed independently of a container’s writable layer.

Restart policy: Rule controlling whether Docker attempts to start a stopped container.

Resource limit: Boundary placed on CPU, memory, or another host resource.

Health check: Test used to assess whether an application is functioning properly.

docker exec: Command that starts an additional process in a running container.

Graceful shutdown: Controlled termination allowing cleanup and data flushing.

Exit code: Numeric result returned by a terminated process.

Image tag: Human-readable image version label, such as 2.1.

Least privilege: Security principle of granting only the permissions required.

Review Questions

Short Answer Questions

  1. What is the relationship between a Docker image and a container?
  2. Why does a container normally stop when its main process exits?
  3. What is the difference between docker stop and docker kill?
  4. What information can docker inspect provide?
  5. Why are user-defined networks useful for multi-container applications?

Long Answer Questions

  1. Explain the major Docker container lifecycle states and the commands that cause transitions between them.
  2. Describe how host ports, container ports, and application bind addresses interact.
  3. Explain how environment variables, image defaults, and secret-management practices should be combined.
  4. Discuss a systematic method for diagnosing a container that repeatedly exits.
  5. Explain the operational benefits and risks of CPU limits, memory limits, and restart policies.

Analytical / Application Questions

  1. An API container is running, but requests to the host port fail. Develop a diagnostic sequence that distinguishes a port conflict, incorrect mapping, application binding error, firewall issue, and application readiness issue.
  2. Design a Docker command set for an API and database that uses a user-defined network, controlled port exposure, runtime configuration, resource limits, and a suitable restart policy. Justify each decision.
  3. A service restarts continuously after deployment and consumes increasing memory before termination. Analyze the evidence you would collect and propose corrective actions at the application, container, and operational levels.

Multiple Choice Questions with Answers

Question 1

Which condition normally causes a Docker container to enter the exited state?

A. The image is present on the host

B. The container’s main process terminates

C. A host port is published

D. A user-defined network is created

Correct Answer: B. The container’s main process terminates

Question 2

Which command lists both running and stopped containers?

A. docker images

B. docker ps -a

C. docker network ls

D. docker top

Correct Answer: B. docker ps -a

Question 3

What does -p 8080:80 normally configure?

A. Container port 8080 mapped to host port 80

B. Host port 8080 mapped to container port 80

C. Two container ports connected internally

D. A memory limit of 8080 MiB

Correct Answer: B. Host port 8080 mapped to container port 80

Question 4

Which command starts an additional process inside a running container?

A. docker exec

B. docker create

C. docker rm

D. docker tag

Correct Answer: A. docker exec

Question 5

Which option supplies a runtime environment variable?

A. --volume

B. --network

C. --env

D. --memory

Correct Answer: C. --env

Question 6

What is the main purpose of docker logs?

A. To display image layers

B. To show data written to the container’s standard output and error

C. To remove old log files from the host

D. To list available networks

Correct Answer: B. To show data written to the container’s standard output and error

Question 7

Which command requests graceful termination before force may be required?

A. docker pause

B. docker stop

C. docker kill

D. docker inspect

Correct Answer: B. docker stop

Question 8

What is a principal risk of using the latest image tag in production?

A. It prevents all port publishing

B. It always disables networking

C. It may refer to different image content over time

D. It limits memory to zero

Correct Answer: C. It may refer to different image content over time

Question 9

Which option limits a container’s CPU capacity approximately to one CPU?

A. --cpus=1.0

B. --ports=1.0

C. --memory=1.0

D. --processes=1.0

Correct Answer: A. --cpus=1.0

Question 10

Why is a user-defined Docker network generally preferred for communicating services?

A. It automatically stores application data permanently

B. It provides controlled communication and name-based service discovery

C. It removes the need for application readiness checks

D. It converts containers into virtual machines

Correct Answer: B. It provides controlled communication and name-based service discovery

Containerization with Docker
Chapter 3 of 15