Running and Managing ContainersIntroductionCreating an image is only the beginning of container-based application delivery. A container becomes useful when it is started with appropriate networking, configuration, resource controls, lifecycle behaviour, and operational access. Running a container therefore involves more than issuing docker run; it requires translating application requirements into explicit runtime policies. A container is an isolated process, or group of processes, running on a host operating system. Its filesystem, network namespace, process identifiers, environment, resource controls, and lifecycle are configured by the container runtime. Docker provides commands and options for managing these properties, while the Linux kernel supplies mechanisms such as namespaces, control groups (cgroups), capabilities, and signals. This chapter explains how to configure and operate containers reliably. It focuses on ports, environment variables, commands and entrypoints, restart policies, signals, health checks, CPU and memory limits, interactive execution, and graceful termination. Learning ObjectivesBy the end of this chapter, you should be able to: - publish container ports safely and distinguish host ports from container ports;
- configure environment variables and identify their security limitations;
- explain the difference between an image
ENTRYPOINT and CMD; - apply restart policies appropriately;
- inspect health status and distinguish health from process existence;
- apply CPU and memory constraints;
- execute commands inside running containers;
- explain signal delivery and configure graceful termination;
- troubleshoot common container runtime problems.
1. The Container Runtime ModelThe command below starts a container from an image: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] Docker creates a container configuration, allocates namespaces and cgroups, prepares a writable container layer, configures networking, and starts the container’s main process. That process is conventionally called PID 1 inside the container. The container normally remains running while this process exists. If the main process exits, the container enters an exited state, even if files remain in its writable layer. Useful lifecycle commands include: | Command | Purpose |
|---|
docker ps | List running containers | docker ps -a | List running and stopped containers | docker start NAME | Start an existing stopped container | docker stop NAME | Request graceful termination | docker restart NAME | Stop and start a container | docker rm NAME | Remove a stopped container | docker inspect NAME | Display detailed configuration and state | docker logs NAME | Display standard output and error |
A container is ephemeral in the operational sense: its writable layer should not be treated as the primary location for important data. Persistent data should normally be stored in volumes or external services. 2. Port Configuration and PublishingContainers commonly listen on ports inside their own network namespace. A process listening on container port 8080 is not automatically reachable through the host’s port 8080. Port publishing creates a forwarding rule between a host address and a container port. docker run -d --name web -p 8080:80 nginx Here, 8080 is the host port and 80 is the container port. A request to the host’s port 8080 is forwarded to port 80 in the web container. The long form makes the mapping clearer: docker run -d --name web -p 127.0.0.1:8080:80 nginx Binding to 127.0.0.1 restricts access to the local host. By contrast, publishing without a host address commonly binds on all host interfaces, making the service potentially reachable from other networks subject to firewall rules. Docker also supports UDP and explicit host IP addresses: docker run -d -p 192.0.2.10:5353:53/udp dns-image The EXPOSE instruction in a Dockerfile documents intended container ports, but it does not publish them. Publishing is a runtime decision. The -P option publishes exposed ports using automatically selected host ports, which is convenient for testing but less predictable for operational systems. Port-design principles- Publish only ports that must be accessed from outside the container network.
- Bind administrative services to localhost or a protected interface when possible.
- Avoid host-port collisions.
- Document whether a protocol is TCP or UDP.
- Treat network publication as an access-control decision, not merely a connectivity setting.
3. Environment Variables and Runtime ConfigurationEnvironment variables provide configuration without rebuilding an image: docker run -d \
--name api \
-e APP_MODE=production \
-e DATABASE_HOST=db \
api-image:1.0 A value can be read from a host variable: docker run --env DATABASE_HOST="$DATABASE_HOST" api-image:1.0 An environment file is useful for repeatable local configuration: docker run --env-file ./api.env api-image:1.0 Environment variables are appropriate for non-sensitive settings such as log levels, service names, feature flags, and connection endpoints. They are not a secure secret store. Values may appear in process inspection output, container metadata, diagnostic reports, shell history, or logs. Passwords and tokens should be supplied through a secrets-management mechanism, protected files, or an orchestrator’s secret facility. Environment variables are strings. Applications must validate and convert them, for example converting WORKERS=4 into an integer and rejecting invalid values. Configuration should have documented defaults, explicit required values, and safe handling of missing or malformed input. 4. Commands, Entrypoints, and PID 1A Docker image may define an entrypoint and a default command. Conceptually: ENTRYPOINT identifies the executable that normally runs;CMD supplies default arguments or, when no entrypoint exists, a default command.
For example: FROM python:3.12-slim
WORKDIR /app
COPY server.py .
ENTRYPOINT ["python", "server.py"]
CMD ["--port", "8000"] Running the image uses the default command arguments. Additional arguments replace CMD arguments while preserving the entrypoint: docker run api-image:1.0 --port 9000 An explicit runtime command can override the image’s normal command, depending on the image configuration: docker run --rm api-image:1.0 python --version Use JSON (exec) form for long-running programs: ENTRYPOINT ["/usr/local/bin/service"] The exec form allows the application to receive signals directly. Shell form, such as ENTRYPOINT service, introduces a shell process. If that shell does not use exec, the application may not be PID 1 and may fail to receive termination signals correctly. PID 1 has special responsibilities. It should reap orphaned child processes and respond appropriately to signals. A minimal init process, such as Docker’s --init option where supported, can help with child-process reaping. Applications should still implement proper signal handling. 5. Restart PoliciesRestart policies determine whether Docker automatically starts a container again after termination: docker run -d --restart unless-stopped --name worker worker-image:1.0 Common policies are: | Policy | Behaviour |
|---|
no | Do not restart automatically; default | on-failure | Restart only when the process exits unsuccessfully | on-failure:N | Retry up to N times | always | Restart whenever it stops, including after daemon restart | unless-stopped | Restart unless it was explicitly stopped |
A restart policy is not a substitute for diagnosing failures. An application with a persistent configuration error may enter a restart loop. Restart behaviour should be selected according to service semantics. A batch job may need on-failure, while a continuously available service may use unless-stopped. Restart policies are local Docker runtime controls. In clustered environments, an orchestrator such as Kubernetes or Docker Swarm may provide a broader reconciliation model. Policies should therefore be documented at the level where they are enforced. 6. Health Checks and Service ReadinessA running container only proves that its main process has not exited. It does not prove that the application is ready to serve requests. A health check tests an operational condition: HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl --fail http://localhost:8080/health || exit 1 A runtime health check can also be supplied: docker run -d --health-cmd='curl --fail http://localhost:8080/health || exit 1' \
--health-interval=30s api-image:1.0 Docker records states such as starting, healthy, and unhealthy. The health command must return exit status 0 for success; a non-zero status indicates failure. The check should be lightweight, deterministic, and targeted at a meaningful dependency or application function. Health checks have limitations. A check that only tests an open TCP port may miss broken application logic. A check that depends on an external database may mark the application unhealthy when the application itself is functioning but a dependency is unavailable. Health checks should distinguish liveness from readiness where the platform supports both concepts. Inspect health information with: docker inspect --format='{{json .State.Health}}' api7. CPU and Memory Resource ControlsContainers share host resources unless limits are applied. Resource controls improve isolation and reduce the risk that one workload consumes all available capacity. Memorydocker run -d --memory=512m --memory-reservation=256m api-image:1.0 --memory establishes a hard memory limit. --memory-reservation expresses a softer target used under contention. If a process exceeds an enforceable memory limit, the kernel may invoke the out-of-memory mechanism and terminate a process. This is not equivalent to application-level memory management; the application should monitor its own heap, buffers, and workload.
CPUdocker run -d --cpus=1.5 api-image:1.0 The value 1.5 represents an allocation equivalent to approximately one and a half logical CPUs under the runtime’s CPU quota model; actual performance depends on host scheduling and contention. Other controls include --cpus, CPU shares, and explicit CPU sets. CPU shares express relative weight during contention rather than a guaranteed speed. Resource limits should be tested under realistic load. A limit that is too low can cause timeouts, garbage-collection pressure, or failed health checks; a limit that is too high can reduce host-level isolation. Monitor container statistics: docker stats Resource settings are not security boundaries in every respect. They limit selected consumption but do not eliminate vulnerabilities, noisy-neighbour effects, or denial-of-service risks. Least privilege, image security, network controls, and host hardening remain necessary. 8. Executing Commands in Running Containersdocker exec starts an additional process in an already running container:
docker exec -it api sh The options mean interactive input (-i) and a pseudo-terminal (-t). A non-interactive diagnostic command is often preferable in automation: docker exec api printenv APP_MODE
docker exec api cat /proc/1/status Execution access is useful for inspection and emergency diagnosis, but manual changes are usually ephemeral and difficult to audit. If a configuration change is required permanently, update the image, configuration source, or deployment definition. Production access should be authenticated, authorized, logged, and limited according to role. docker attach connects to the main process’s standard streams and is different from docker exec, which starts a new process. Detaching incorrectly from an attached terminal can affect the process; operational teams should understand the terminal escape sequence and prefer logs or exec for diagnosis.
9. Signals and Graceful TerminationA graceful shutdown allows an application to stop accepting new work, finish or cancel active work, close connections, flush buffers, and exit with an appropriate status. Docker’s normal stop process is conceptually: - send a termination signal, normally
SIGTERM, to the container’s main process; - wait for the configured grace period;
- send
SIGKILL if the process remains alive.
docker stop --time 30 api The default grace period depends on Docker and platform configuration; specifying it explicitly is clearer for important services. SIGKILL cannot be caught or handled, so data may be lost if the application exceeds the grace period. A service should handle SIGTERM and stop accepting new work. In a shell entrypoint, use exec so the application replaces the shell: #!/bin/sh
set -eu
exec /usr/local/bin/service "$@" Applications should be idempotent during shutdown: receiving a termination request more than once should not corrupt state. Shutdown time must account for load balancers, connection draining, transaction completion, and dependency ordering. docker kill sends a signal immediately, with SIGKILL commonly used by default. It is appropriate when a process is unresponsive but should not be the normal deployment procedure.
10. Integrated Applied Case: A Containerised Web APIBackgroundThis illustrative case concerns a small web API packaged as orders-api:2.0. It listens on port 8080, requires a database host and application mode, exposes /health, and must finish active requests during deployment. ProblemThe first deployment used: docker run -d -p 8080:8080 orders-api:2.0 The container restarted unpredictably, could not be reached from a monitoring host, and occasionally lost requests during updates. No memory limit or health check was configured. AnalysisInspection showed that the image’s shell-form entrypoint did not forward SIGTERM reliably. The application also required DATABASE_HOST, but the variable was absent. The host port was published on all interfaces, although only the reverse proxy needed access. Finally, the service could become process-alive before its database connection pool was ready. ApproachThe deployment was changed to: docker run -d \
--name orders-api \
--restart unless-stopped \
-p 127.0.0.1:8080:8080 \
--env-file ./orders-api.env \
--memory=768m \
--cpus=1.0 \
--health-cmd='curl --fail http://localhost:8080/health || exit 1' \
--health-interval=20s \
--health-timeout=4s \
--health-retries=3 \
--stop-timeout=30 \
orders-api:2.0 The image entrypoint was rewritten in exec form, and the application was modified to handle SIGTERM. The reverse proxy remained the only externally exposed component. OutcomeThe service became reachable through the intended path, unhealthy startup states were visible, repeated crashes were automatically recovered, and deployments allowed active requests time to complete. Configuration remained outside the image, while sensitive credentials were moved to a protected secret mechanism rather than a committed environment file. Learning Points- Connectivity, readiness, and process existence are different concerns.
- A restart policy improves availability but cannot repair a defective application.
- Correct PID 1 behaviour is central to graceful termination.
- Port binding and secret handling are security decisions.
- Resource limits should be introduced with monitoring and load testing.
11. Operational Best Practices and LimitationsUse immutable, versioned images and avoid modifying production containers manually. Keep runtime configuration explicit and validate it at startup. Log to standard output and error so that the container runtime can collect logs. Set resource limits based on measurements rather than guesses. Define health checks that reflect real service usefulness. Test shutdown behaviour, including slow requests and dependency failures. Do not assume that containers provide virtual machines’ level of isolation. Containers share the host kernel, so kernel vulnerabilities, excessive privileges, mounted sockets, and unsafe capabilities can create serious exposure. Avoid privileged mode unless it is justified, use a non-root user where possible, restrict Linux capabilities, and never mount the Docker socket into an untrusted container without understanding the implications. Contemporary platforms increasingly separate liveness, readiness, and startup checks, and use declarative reconciliation rather than standalone Docker commands. The underlying principles remain the same: make dependencies explicit, constrain resources, observe actual state, and design the application to respond correctly to lifecycle events. Chapter SummaryRunning a container involves configuring its network exposure, runtime configuration, process model, restart behaviour, health status, resources, operational access, and shutdown procedure. Port publishing maps a host address and port to a container port; it should be restricted to required interfaces. Environment variables are useful for non-secret configuration but should not replace secret-management systems. ENTRYPOINT defines the normal executable, while CMD supplies defaults; exec form improves signal delivery. Restart policies can recover failed services but do not replace diagnosis. Health checks provide evidence about application readiness beyond simple process existence. CPU and memory controls improve isolation and require measurement. docker exec supports controlled diagnosis, whereas permanent changes belong in images or deployment configuration. Graceful termination depends on signal-aware applications, correct PID 1 behaviour, and an adequate stop timeout. Key TermsContainer: An isolated process environment created from an image. Container image: An immutable package containing application code, dependencies, and metadata. Port publishing: Forwarding traffic from a host address and port to a container port. Environment variable: A string-based runtime configuration value supplied to a process. ENTRYPOINT: Image metadata identifying the primary executable. CMD: Default command or arguments associated with an image. PID 1: The first process in a container’s process namespace. Restart policy: A rule controlling automatic container restart behaviour. Health check: A command used to assess whether an application is functioning as intended. Readiness: The condition that a service can accept useful work. Liveness: The condition that a service process is operating sufficiently to continue. cgroup: A Linux kernel mechanism for grouping and controlling processes and resources. Memory limit: An upper bound on memory consumption enforced for a container. CPU quota: A runtime control limiting or proportioning CPU access. docker exec: A command that starts an additional process in a running container.
Signal: An operating-system notification sent to a process, such as SIGTERM. Graceful termination: Controlled shutdown that allows work and resources to be completed safely. SIGKILL: An uncatchable signal that immediately terminates a process.
Least privilege: Providing only the permissions and resources required for operation. Review QuestionsShort Answer Questions- What is the difference between a host port and a container port?
- Why should environment variables not normally be used as a secret-management system?
- What roles do
ENTRYPOINT and CMD play in an image? - Why does a running container not necessarily indicate a healthy application?
- What normally happens after Docker sends
SIGTERM during docker stop?
Long Answer Questions- Explain how Docker port publishing works and discuss the security implications of binding to all host interfaces.
- Compare the principal Docker restart policies and recommend policies for a web service and a batch job.
- Explain PID 1, exec-form entrypoints, and their relationship to signal handling.
- Discuss how CPU and memory limits affect reliability, performance, and host isolation.
- Describe a complete operational procedure for deploying a health-checked API with graceful shutdown.
Analytical / Application Questions- A service is reported as running, but clients receive errors for the first 30 seconds after startup. Which runtime mechanisms should be examined, and how would you improve the configuration?
- A container repeatedly restarts after a configuration change. Develop a diagnostic sequence using logs, inspection, health information, and restart-policy analysis.
- An API loses requests during deployment because it is killed after 10 seconds. Identify the likely lifecycle causes and propose changes to the application, entrypoint, load balancer, and stop timeout.
Multiple Choice Questions with AnswersQuestion 1In docker run -p 8080:80 nginx, which port belongs to the container? A. Port 22 B. Port 80 C. Port 8080 D. The dynamically assigned port Correct Answer: B. Port 80 Question 2What does EXPOSE 8080 in a Dockerfile do by itself? A. Publishes host port 8080 B. Creates a firewall rule C. Documents an intended container port D. Binds the port to localhost Correct Answer: C. Documents an intended container port Question 3Which Dockerfile instruction identifies the normal executable for a container? A. WORKDIR B. EXPOSE C. ENV D. ENTRYPOINT Correct Answer: D. ENTRYPOINT Question 4Which restart policy restarts a container unless it was explicitly stopped? A. no B. on-failure C. always D. unless-stopped Correct Answer: D. unless-stopped Question 5What exit status should a successful Docker health-check command return? A. 0 B. 1 C. 127 D. Any non-zero status Correct Answer: A. 0 Question 6Which option establishes a hard memory limit in the example configuration? A. --memory=512m B. --cpus=1.5 C. --memory-reservation=256m D. --health-timeout=5s Correct Answer: A. --memory=512m Question 7What is the primary purpose of docker exec -it api sh? A. Build a new image B. Start an interactive shell in a running container C. Publish a new port D. Restart the Docker daemon Correct Answer: B. Start an interactive shell in a running container Question 8Which signal is normally sent first by docker stop? A. SIGKILL B. SIGSTOP C. SIGTERM D. SIGHUP Correct Answer: C. SIGTERM Question 9Why is exec-form ENTRYPOINT generally preferred for long-running applications? A. It automatically creates a volume B. It allows the application to receive signals more directly C. It disables health checks D. It guarantees unlimited CPU Correct Answer: B. It allows the application to receive signals more directly Question 10What is the main limitation of a restart policy? A. It cannot restart any process B. It replaces the need for application monitoring C. It may repeatedly restart an application with a persistent configuration error D. It always disables logging Correct Answer: C. It may repeatedly restart an application with a persistent configuration error
|