| |

LFCA 122 ๐Ÿง Real-World Scenario โ€” Deploying a Container

Containers are not virtual machines. A container is a processโ€”or a small group of processesโ€”running on the host kernel, isolated by namespaces and constrained by cgroups. This distinction matters for the deployment workflow. There is no boot sequence, no init system, no hypervisor. A container starts in milliseconds, runs until its main process exits, and disappears. Deploying a container means pulling an image, running it with the right configuration, exposing the right ports, mounting the right volumes, and verifying that it works from outside.

This chapter walks through a complete container deployment on a Linux host. You will install a container runtime, run a container from a registry image, verify that it is running and listening, expose it to the network, persist its data with a volume, and inspect it when something goes wrong. The scenario is a typical production task: deploying a web service in a container and making it reachable.

By the end, you will have a repeatable deployment procedure and the diagnostic commands to verify each step.

Key point: A container’s lifecycle is tied to its main process. When the process exits, the container stops. There is no service manager inside the container by default; the runtime ensures the process is running. Persistence lives outside the containerโ€”in volumes, bind mounts, and the image registry. The container itself is disposable.


Why container deployment matters

The packaging problem. An application that runs on a developer’s laptop may fail on a server because of a missing library, a different language version, or an environment variable that was set locally but not in production. A container image packages the application with its runtime, its libraries, and its default configuration. The image is the same everywhere it runs. The only differences are the environment variables and volumes supplied at runtime.

The isolation problem. A container runs in its own namespace. It has its own process tree, its own network stack, its own filesystem view. A container that crashes does not take down the host. A container that is compromised has a limited view of the system. The isolation is not as strong as a virtual machine, but it is sufficient for most workloads, and it is lighter and faster.

The lifecycle problem. A container starts, runs, and stops. It does not persist by default. Files written inside the container are lost when it is removed. This is a feature, not a bug: it means a container can be replaced without losing the application’s state, as long as the state is stored in a volume or an external service. Deployments become replace operations, not patch operations.

The orchestration problem. A single container on a single host is simple. Dozens of containers across multiple hosts require orchestrationโ€”Kubernetes, Docker Swarm, Nomad. But orchestration builds on the same primitives: images, containers, volumes, networks. Understanding the single-host deployment is a prerequisite for understanding the orchestrated one.

The LFCA domain coverage. The LFCA exam covers container fundamentals as part of the operations domain . The exam expects candidates to understand what a container is, how it differs from a virtual machine, and the basic commands for managing containers. This chapter provides the practical context for those commands.


a. Installing a container runtime and pulling an image

The container runtime is the software that pulls images, creates containers, and manages their lifecycle. Docker is the most widely used runtime. Podman is a daemonless alternative that is compatible with the Docker CLI. On a Debian or Ubuntu system, Docker is installed from the distribution repository or from Docker’s own repository.

# Debian/Ubuntu
sudo apt update
sudo apt install docker.io -y

# RHEL/Fedora
sudo dnf install podman -y

After installation, verify that the runtime is working.

docker version
docker info

The docker version command shows the client and server versions. The docker info command shows the runtime configuration: storage driver, cgroup driver, number of containers, and more. If the server is not running, docker info reports an error, and the daemon must be started.

sudo systemctl start docker
sudo systemctl enable docker

The daemon must be running for docker commands to work. Enabling it ensures it starts at boot.

With the runtime installed, the next step is pulling an image from a registry. The default registry is Docker Hub. An image is identified by repository:tag.

docker pull nginx:alpine

The nginx:alpine image is the official Nginx image based on Alpine Linux. It is smallโ€”around 50 MBโ€”and contains the Nginx web server. The pull command downloads the image layers and stores them locally.

docker images

The docker images command lists the locally available images with their repository, tag, image ID, and size. An image that has been pulled once is available for any container that uses it.


b. Running a container with ports and volumes

The docker run command creates and starts a container from an image. Its options configure the container’s runtime environment.

docker run -d \
  --name webserver \
  -p 8080:80 \
  nginx:alpine

The flags:

  • -d runs the container in detached mode (in the background).
  • --name webserver assigns a name to the container. Without it, Docker generates a random name.
  • -p 8080:80 maps port 8080 on the host to port 80 in the container. Traffic to the host’s port 8080 is forwarded to the container’s port 80.
  • nginx:alpine is the image to run.

The container starts, and the Nginx process begins listening on port 80 inside the container’s network namespace. The host’s port 8080 is connected to it.

docker ps

The docker ps command lists running containers with their ID, image, command, status, and port mappings. A container that has exited is not shown unless -a is used.

docker ps -a

Persistence is configured with volumes or bind mounts. A volume is managed by Docker and stored in a Docker-managed directory. A bind mount maps a host directory into the container.

docker run -d \
  --name webserver \
  -p 8080:80 \
  -v /srv/www:/usr/share/nginx/html:ro \
  nginx:alpine

The -v /srv/www:/usr/share/nginx/html:ro option mounts the host’s /srv/www directory at /usr/share/nginx/html inside the container, read-only. Files placed in /srv/www on the host appear as the web content served by Nginx.

A named volume is created and managed by Docker:

docker volume create webdata
docker run -d \
  --name webserver \
  -p 8080:80 \
  -v webdata:/usr/share/nginx/html \
  nginx:alpine

The webdata volume persists independently of the container. If the container is removed and recreated with the same volume, the data is still there.


c. Verification, inspection, and troubleshooting

After the container is running, the first verification is that the process is alive and the port is mapped.

docker ps

The output shows the container ID, image, command, status (e.g., Up 5 minutes), and port mapping (0.0.0.0:8080->80/tcp).

The second verification is that the application responds. From the host:

curl -I http://localhost:8080

The curl -I command sends a HEAD request and prints the response headers. A 200 OK response confirms that Nginx is serving content and the port mapping is working.

From another machine on the network:

curl -I http://server-ip:8080

If the local request works but the remote request fails, the problem is likely the host firewall. Docker manipulates iptables rules to forward traffic to containers, but if the host firewall blocks the port before Docker’s rules apply, the traffic never reaches the container .

sudo ufw status
sudo firewall-cmd --list-all

The firewall must allow the port that is published. On systems using ufw, sudo ufw allow 8080/tcp opens the port. On systems using firewalld, sudo firewall-cmd --add-port=8080/tcp --permanent && sudo firewall-cmd --reload does the same.

When the container is not behaving as expected, the first diagnostic command is docker logs.

docker logs webserver
docker logs -f webserver

The docker logs command shows the stdout and stderr of the container’s main process. The -f flag follows the output in real time. Nginx logs access requests and errors here.

The second diagnostic command is docker inspect.

docker inspect webserver

The docker inspect command returns a JSON document with the container’s configuration: environment variables, mounts, network settings, port mappings, and state. It is the most complete source of information about a container. The --format flag extracts specific fields:

docker inspect --format '{{.State.Status}}' webserver
docker inspect --format '{{.NetworkSettings.IPAddress}}' webserver
docker inspect --format '{{json .Mounts}}' webserver

If the container has exited, docker ps -a shows its exit code. An exit code of 0 means the process completed successfully. A non-zero exit code indicates an error. The logs usually explain why.

docker ps -a
docker logs webserver

To run a command inside a running container, use docker exec.

docker exec -it webserver sh

The -it flags allocate a pseudo-TTY and keep stdin open, providing an interactive shell. Inside the container, you can inspect files, check the network configuration, and run diagnostic commands. The sh command is used because Alpine-based images do not include bash by default .

To stop and remove a container:

docker stop webserver
docker rm webserver

The docker stop command sends SIGTERM to the main process, waits for it to exit gracefully, and sends SIGKILL if it does not. The docker rm command removes the stopped container. The image remains and can be used to start a new container.


Complete Example Session

# ============================================
# PART 1: INSTALL CONTAINER RUNTIME
# ============================================
sudo apt update
sudo apt install docker.io -y
sudo systemctl start docker
sudo systemctl enable docker
# ============================================
# PART 2: VERIFY RUNTIME
# ============================================
docker version
docker info
# ============================================
# PART 3: PULL AN IMAGE
# ============================================
docker pull nginx:alpine
docker images
# ============================================
# PART 4: RUN A CONTAINER
# ============================================
docker run -d --name webserver -p 8080:80 nginx:alpine
docker ps
# ============================================
# PART 5: VERIFY LOCALLY
# ============================================
curl -I http://localhost:8080
# ============================================
# PART 6: CONFIGURE FIREWALL
# ============================================
sudo ufw allow 8080/tcp
sudo ufw status
# ============================================
# PART 7: VERIFY REMOTELY
# ============================================
# On another machine:
curl -I http://server-ip:8080
# ============================================
# PART 8: MOUNT A VOLUME
# ============================================
mkdir -p /srv/www
echo "<h1>Hello from container</h1>" | sudo tee /srv/www/index.html
docker run -d \
  --name webserver \
  -p 8080:80 \
  -v /srv/www:/usr/share/nginx/html:ro \
  nginx:alpine
# ============================================
# PART 9: INSPECT AND LOG
# ============================================
docker logs webserver
docker inspect --format '{{.State.Status}}' webserver
docker exec -it webserver sh
# ============================================
# PART 10: STOP AND REMOVE
# ============================================
docker stop webserver
docker rm webserver
docker ps -a

The ten parts covered installing the runtime, verifying it, pulling an image, running a container, local verification, firewall configuration, remote verification, volume mounting, inspection and logs, and stopping and removing the container.


Quick Reference

Container Lifecycle Commands

CommandPurpose
docker pull imageDownload an image
docker imagesList local images
docker run -d --name x -p h:c imageRun a container
docker psList running containers
docker ps -aList all containers
docker stop nameStop a container
docker rm nameRemove a stopped container
docker rmi imageRemove an image

docker run Flags

FlagPurpose
-dDetached mode
--nameAssign a container name
-p host:containerPublish a port
-v host:containerBind mount
-v volume:containerNamed volume
-e KEY=valueSet environment variable
--restartRestart policy

Diagnostic Commands

CommandPurpose
docker logs nameContainer stdout/stderr
docker logs -f nameFollow logs
docker inspect nameFull container config
docker exec -it name shShell inside container
docker statsLive resource usage
docker top nameProcesses in container

Port and Firewall

TaskCommand
Publish port-p 8080:80
Check listeningss -tulpn | grep 8080
Open UFW portsudo ufw allow 8080/tcp
Open firewalld portsudo firewall-cmd --add-port=8080/tcp
Check Docker iptablessudo iptables -t nat -L

Best Practices

โœ… Do This:

# Use specific image tags, not :latest
docker pull nginx:1.27-alpine                              # โœ…

# Name your containers
docker run -d --name webserver -p 8080:80 nginx:alpine     # โœ…

# Mount data as volumes, not inside the container
docker run -v /srv/www:/usr/share/nginx/html:ro ...        # โœ…

# Verify locally before verifying remotely
curl -I http://localhost:8080                              # โœ…

# Check logs when the container is not working
docker logs webserver                                      # โœ…

# Use a restart policy for production
docker run -d --restart unless-stopped ...                 # โœ…

# Open the firewall port explicitly
sudo ufw allow 8080/tcp                                    # โœ…

โŒ Don’t Do This:

# Don't run containers without a name in production
docker run -d nginx:alpine                                 # โš ๏ธ random name

# Don't store data inside the container
# (it is lost when the container is removed)               # โŒ

# Don't expose unnecessary ports
docker run -p 0.0.0.0:3306:3306 mysql                      # โŒ

# Don't skip the firewall check
# (local works, remote fails)                              # โŒ

# Don't use :latest in production
docker pull nginx:latest                                   # โš ๏ธ

# Don't exec into a container to make persistent changes
docker exec -it webserver sh
# Changes are lost when the container is recreated          # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Remote access failsHost firewall blockingufw allow 8080/tcp
Data lost on container removalData written inside containerUse volumes
Container exits immediatelyMain process exitsCheck docker logs
Port already in useAnother process on host portUse different host port
Image not foundWrong tag or registryCheck docker pull output
Cannot exec with bashAlpine has no bashUse sh
Container not restartingNo restart policyAdd --restart

Real-World Examples

1. Run Nginx

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

2. Mount Web Content

docker run -d -v /srv/www:/usr/share/nginx/html:ro nginx:alpine

3. Set Environment Variable

docker run -d -e MYSQL_ROOT_PASSWORD=secret mysql:8

4. Persistent Volume

docker volume create dbdata
docker run -d -v dbdata:/var/lib/mysql mysql:8

5. Restart Policy

docker run -d --restart unless-stopped nginx:alpine

6. Check Logs

docker logs -f web

7. Inspect Container

docker inspect --format '{{.NetworkSettings.IPAddress}}' web

8. Shell Inside Container

docker exec -it web sh

9. List All Containers

docker ps -a

10. Remove Container and Image

docker stop web && docker rm web
docker rmi nginx:alpine

Visual

Container vs Virtual Machine

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  VIRTUAL MACHINE                                            โ”‚
โ”‚                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  Application                                        โ”‚    โ”‚
โ”‚  โ”‚  Libraries                                          โ”‚    โ”‚
โ”‚  โ”‚  Guest OS (full kernel)                             โ”‚    โ”‚
โ”‚  โ”‚  Hypervisor                                         โ”‚    โ”‚
โ”‚  โ”‚  Host OS                                            โ”‚    โ”‚
โ”‚  โ”‚  Hardware                                           โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                             โ”‚
โ”‚  Boots in seconds. Full OS per VM. Heavy.                   โ”‚
โ”‚                                                             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  CONTAINER                                                  โ”‚
โ”‚                                                             โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  Application                                        โ”‚    โ”‚
โ”‚  โ”‚  Libraries                                          โ”‚    โ”‚
โ”‚  โ”‚  (shares host kernel)                               โ”‚    โ”‚
โ”‚  โ”‚  Container runtime                                  โ”‚    โ”‚
โ”‚  โ”‚  Host OS                                            โ”‚    โ”‚
โ”‚  โ”‚  Hardware                                           โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                             โ”‚
โ”‚  Starts in milliseconds. Shares kernel. Lightweight.        โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Container Lifecycle

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  CONTAINER LIFECYCLE                                        โ”‚
โ”‚                                                             โ”‚
โ”‚  docker pull nginx:alpine                                   โ”‚
โ”‚    โ”‚                                                        โ”‚
โ”‚    โ–ผ                                                        โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                            โ”‚
โ”‚  โ”‚   IMAGE     โ”‚  (read-only, stored locally)               โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                            โ”‚
โ”‚         โ”‚ docker run                                        โ”‚
โ”‚         โ–ผ                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                            โ”‚
โ”‚  โ”‚  CONTAINER  โ”‚  (writable layer on top of image)          โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                            โ”‚
โ”‚         โ”‚ process exits                                     โ”‚
โ”‚         โ–ผ                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                            โ”‚
โ”‚  โ”‚  STOPPED    โ”‚  (container still exists)                  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                            โ”‚
โ”‚         โ”‚ docker rm                                         โ”‚
โ”‚         โ–ผ                                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                                            โ”‚
โ”‚  โ”‚  REMOVED    โ”‚  (writable layer discarded, image remains) โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                                            โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Port Mapping

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  PORT MAPPING: -p 8080:80                                   โ”‚
โ”‚                                                             โ”‚
โ”‚  Host Network Namespace                                     โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  :8080  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                             โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                           โ”‚                                 โ”‚
โ”‚                           โ”‚ Docker port forwarding          โ”‚
โ”‚                           โ”‚                                 โ”‚
โ”‚  Container Network Namespace                               โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  :80  โ—€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                            โ”‚    โ”‚
โ”‚  โ”‚                                                     โ”‚    โ”‚
โ”‚  โ”‚  nginx process listening on port 80                 โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                                                             โ”‚
โ”‚  Client connects to host:8080 โ†’ forwarded to container:80   โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Volume vs Bind Mount

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  BIND MOUNT: -v /srv/www:/usr/share/nginx/html              โ”‚
โ”‚                                                             โ”‚
โ”‚  Host: /srv/www                                             โ”‚
โ”‚       โ”‚                                                     โ”‚
โ”‚       โ””โ”€โ”€โ–ถ Container: /usr/share/nginx/html                 โ”‚
โ”‚                                                             โ”‚
โ”‚  Host directory is mapped directly into the container.      โ”‚
โ”‚  Files created on either side are visible on both.          โ”‚
โ”‚                                                             โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚                                                             โ”‚
โ”‚  NAMED VOLUME: -v webdata:/usr/share/nginx/html             โ”‚
โ”‚                                                             โ”‚
โ”‚  Docker-managed storage                                     โ”‚
โ”‚       โ”‚                                                     โ”‚
โ”‚       โ””โ”€โ”€โ–ถ Container: /usr/share/nginx/html                 โ”‚
โ”‚                                                             โ”‚
โ”‚  Volume is managed by Docker.                               โ”‚
โ”‚  Persists independently of the container.                   โ”‚
โ”‚  Path on the host is under /var/lib/docker/volumes/.        โ”‚
โ”‚                                                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Runtime installapt install docker.io or dnf install podman
Daemon startsystemctl start docker
Pull imagedocker pull nginx:alpine
Run containerdocker run -d --name web -p 8080:80 nginx:alpine
List runningdocker ps
List alldocker ps -a
Port mapping-p host:container
Bind mount-v /host:/container
Named volume-v volume:/container
Logsdocker logs name
Inspectdocker inspect name
Execdocker exec -it name sh
Stopdocker stop name
Removedocker rm name

Key takeaways:

  • A container is a process, not a virtual machine. It shares the host kernel and is isolated by namespaces and cgroups. It starts in milliseconds and exits when its main process exits. There is no init system inside the container.
  • Container data is ephemeral unless stored in a volume. Files written inside the container are lost when the container is removed. Volumes and bind mounts provide persistence.
  • Port mapping connects the host to the container. -p 8080:80 forwards traffic from the host’s port 8080 to the container’s port 80. The container’s network namespace is separate from the host’s.
  • The host firewall can block published ports. Docker manipulates iptables rules to forward traffic to containers, but if the host firewall blocks the port first, the traffic never reaches Docker’s rules. Open the port explicitly with ufw or firewalld.
  • docker logs is the first diagnostic command. It shows the stdout and stderr of the container’s main process. When a container exits immediately or behaves unexpectedly, the logs usually explain why.
  • docker inspect shows the full configuration. It returns a JSON document with the container’s state, environment, mounts, network settings, and port mappings. The --format flag extracts specific fields.
  • docker exec runs a command inside a running container. It is useful for inspecting the container’s filesystem and network configuration. Alpine-based images use sh, not bash.
  • Use specific image tags, not :latest. The :latest tag is mutable and can change without warning. A specific tag like nginx:1.27-alpine ensures the same image is used every time.

Remember: Deploying a container is a deployment, not just a docker run. The command starts a process, but the deployment includes the port mapping, the volume configuration, the firewall rules, and the verification that the service is reachable from outside. The container is disposableโ€”it can be removed and recreated without losing the application’s state, as long as the state lives in a volume or an external service. This is the fundamental difference from a virtual machine, and it is what makes containers well-suited to automated deployment. Verify each step: the container is running, the port is mapped, the service responds locally, and the service responds remotely. When something fails, the logs and the inspect output tell you why.



Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!