Docker Architecture, Installation, and Complete Container Lifecycle Management
If you have been hearing about Docker everywhere lately and wondering what all the fuss is about, you are in exactly the right place. Docker has become a non-negotiable skill in the industry. Open any job description for a DevOps engineer, a backend developer, or a cloud architect, and you will find Docker sitting right there in the requirements list. The reason is simple: everyone is moving toward microservices, and containers are how you run microservices. In this post, we are going to go deep into the foundations. We will walk through Docker's internal architecture, get it installed and running, and then spend serious time on the complete container lifecycle. By the end, you will know every major Docker command, understand why certain commands exist, and be able to work with containers confidently on any machine.

Understanding Docker's Architecture
Before you run a single command, it is worth understanding what is actually happening under the hood when you type something like docker run.
Docker follows a client-server architecture with three components:
Docker CLI (the client): This is you, typing commands in your terminal.
REST API: The CLI does not talk directly to Docker's core. It sends HTTP requests through a REST API layer.
Docker Daemon (dockerd): This is the actual server process that does all the real work. It listens for API requests and manages images, containers, networks, and volumes.
So the flow looks like this: you type a command in the Docker CLI, the CLI sends that request through the REST API, and the REST API forwards it to the Docker Daemon, which carries it out.
The critical thing to remember here is that the Docker Daemon must always be running before you can use any Docker commands. If the daemon is not running, every command you type will fail with a message like "Cannot connect to the Docker daemon."
You will see this firsthand when you install Docker and try running a command before starting the service. Docker just refuses to respond. Start the daemon first, then you can work.
Installation and Getting Docker Running
On an Amazon Linux EC2 instance (which is RPM-based), the installation command is:
yum install docker -y
Once installed, you need to start the Docker service. Docker runs as a system service managed by systemd, so you use:
systemctl start docker
To verify that Docker is installed correctly and check which version you have:
docker --version
That's it. Docker is now running. From this point on, as long as this service is active, all your Docker commands will work.
Working with Docker Images
Think of a Docker image the same way you think of an AMI in AWS. An AMI is a copy of an operating system that you use to launch EC2 instances. A Docker image is similar. It is a packaged snapshot of an operating system or application that you use to create containers.
There are two main ways to get images:
Pull them from Docker Hub (the public registry)
Build your own using a Dockerfile (we will cover this in a separate post)
Listing Your Local Images
To see all the images you currently have stored locally on your machine:
docker images
The output shows several columns: the image name, the tag (like a version label), the image ID, when it was created, and its size.
Pulling Images from Docker Hub
To download an image from Docker Hub to your local machine:
docker pull ubuntu
docker pull amazonlinux
When you run this, Docker connects to Docker Hub and downloads the image layer by layer. Once it finishes, the image is available locally and you can create containers from it.
Removing Images
To delete an image from your local machine:
docker rmi <image_id>
You can use the image ID or the image name. Just be aware that if a container is currently using that image, Docker will refuse to delete it. You need to remove the container first.
Cleaning Up Unused Images
Over time, developers and teams tend to pull a lot of images and then just leave them sitting there consuming disk space. Instead of manually hunting down which ones are not being used, there is a single command that handles it:
docker image prune
This deletes all images that are not associated with any running or stopped container. It is very useful in team environments where images pile up quickly.
Creating Containers
An image is just a template. A container is a running instance of that template. The relationship is the same as AMI to EC2 instance: one image can spawn many containers.
Interactive Mode: Create a Container and Log Into It
The most basic way to create a container and immediately jump inside it:
docker run -it --name amazon-linux-container amazonlinux
Breaking this down:
docker run: Create and start a container-it: Interactive TTY mode. This combination of flags means "give me a shell I can type commands into"--name amazon-linux-container: Give this container a human-readable name (otherwise Docker assigns a random one)amazonlinux: The image to use
Once you run this, your prompt changes. You are no longer on your EC2 host machine. You are now inside the container. You can run Linux commands, install software, create files. It behaves exactly like an OS shell.
For example, to install Git inside an Amazon Linux container:
yum install git -y
To install packages in an Ubuntu container:
apt install git -y
The container is its own isolated environment. Whatever you install stays inside that container and does not affect your host machine.
Detached Mode: Create a Container Without Logging Into It
Sometimes you just want a container to be created and running in the background without you being dropped into its shell. For that, you add the -d flag:
docker run -itd --name ubuntu-container ubuntu
The -d flag stands for detached. The container is created and starts running, but your terminal stays on the host machine. You will see a long container ID printed out, and that's it.
This is the mode you will use most often in real-world scenarios.
Exiting Containers Safely
This is one of the most common mistakes people make when starting with Docker.
When you are inside a container's shell, there are two ways to exit:
Option 1: Ctrl+P+Q (the right way)
Press Control, then P, then Q. This detaches your terminal from the container without stopping it. The container keeps running happily in the background. You are just no longer interacting with it.
Option 2: Ctrl+D or typing exit (use with caution)
This ends the shell session. In most cases, this stops the container entirely because the shell was the main process keeping it alive. Come back to find your container in a stopped state.
Get into the habit of using Ctrl+P+Q every single time you want to leave a container.
Listing Containers
Once you start creating multiple containers, you need to keep track of them.
To list only containers that are currently running:
docker ps
To list all containers, including stopped ones:
docker ps -a
The output shows the container ID, the image it was created from, the command it runs, when it was created, its current status, any port mappings, and the container name.
The status column is important. It tells you whether a container is "Up," "Exited," or "Paused."
docker attach vs docker exec: The Important Difference
Once a container is running in the background, you have two ways to log back into it. Understanding the difference between these two commands is a common interview question and something you genuinely need to know.
docker attach
docker attach <container_name_or_id>
This attaches your terminal directly to the main process (PID 1) of the container. The problem is that when you are attached to the main process and you press Ctrl+D or type exit, you kill that main process. When the main process dies, the container stops.
docker exec
docker exec -it <container_name_or_id> /bin/bash
This creates a brand new process inside the already-running container. You get a fresh shell to work in. When you exit this shell, only that new process ends. The container's main process is completely untouched, and the container keeps running.
This is the preferred way to log into a container. Always use docker exec over docker attach. It is safer, cleaner, and does not risk accidentally stopping your container.
Managing the Container Lifecycle
Stopping a Container
docker stop <container_name>
This sends a SIGTERM signal to the container's main process, giving it time to finish up whatever it is doing and shut down cleanly. This is the graceful approach.
Killing a Container
docker kill <container_name>
This sends a SIGKILL signal immediately. No waiting, no cleanup. The process is terminated on the spot. This is the forceful approach.
Which one should you use? Almost always docker stop. It gives the application inside the container a chance to close connections, write pending data, and exit cleanly. Use docker kill only when the container is completely unresponsive and docker stop is not working.
Killing production containers that are running databases or applications is the kind of thing that gets people fired. Use stop unless you have a very good reason not to.
Starting a Stopped Container
docker start <container_name>
This brings a container back from an "Exited" state without recreating it. All the data and changes inside the container are preserved.
Restarting a Container
docker restart <container_name>
Equivalent to stop followed by start. The container gets a fresh start while preserving its state.
Pausing and Unpausing
docker pause <container_name>
docker unpause <container_name>
Pausing freezes all processes inside the container without actually stopping it. The container exists, it is in memory, but nothing inside it is executing. Think of it like pressing the pause button on a video.
When a container is paused, you cannot docker exec into it. You will get an error saying the container is paused. You must unpause it first.
Removing Containers
To remove a stopped container:
docker rm <container_name>
This only works on containers that are already in a stopped state. If the container is running, Docker refuses.
To force-remove a running container (stop and remove in one shot):
docker rm -f <container_name>
This is the equivalent of doing docker kill followed by docker rm. It is useful when you are cleaning up quickly and do not want the extra step.
Inspecting Containers in Depth
Full Container Information
docker inspect <container_name>
This dumps a massive JSON object with everything you could possibly want to know about the container: its ID, when it was created, its current state, whether it is running or paused, the PID of its main process, which image it was built from, the platform it is running on, network settings, mounted volumes, and much more.
When debugging container issues, docker inspect is your go-to command.
Live Resource Usage
docker stats <container_name>
This shows a live, continuously updating view of how much CPU and memory the container is consuming, along with its network I/O and disk I/O. If your container is misbehaving or consuming unexpected resources, this is the first place to look.
One thing to know: docker stats does not return to your prompt on its own. It keeps running and refreshing. Press Ctrl+C to exit it.
Running Processes Inside a Container
docker top <container_name>
This is the Docker equivalent of the top command in Linux. It shows all the processes currently running inside the container without requiring you to log into it.
Container Logs
docker logs <container_name>
This shows the stdout and stderr output from whatever was running inside the container. If your application is printing logs to the terminal, they will appear here. This is essential for debugging why a container crashed or why an application is not behaving correctly.
Copying Files Between Host and Container
You do not always need to log into a container to get files into it. The docker cp command lets you copy files directly:
docker cp hello.txt <container_name>:/tmp
This copies hello.txt from your host machine into the /tmp directory inside the container. To verify it worked, you can docker exec into the container and check:
docker exec -it <container_name> /bin/bash
cd /tmp
ls
Saving and Loading Images as TAR Files
This is not a command you will use constantly, but it is worth knowing when you need it.
If you want to share an image with a teammate who does not have access to Docker Hub, you can package the image into a .tar file:
docker save ubuntu > ubuntu.tar
This creates a ubuntu.tar archive file that contains the complete image. You can then share this file via SCP or any other file transfer method.
On the receiving end, your teammate converts it back into a Docker image with:
docker load -i ubuntu.tar
After running this, docker images will show the ubuntu image available locally.
The ideal way to share images in real-world teams is through Docker Hub. But if you need to transfer images through non-standard channels, save and load are the tools for that job.
Creating an Image from a Running Container
There is one more image creation method beyond Dockerfiles and Docker Hub: the docker commit command.
The workflow looks like this:
Pull a base image (
ubuntu, for example)Create a container from it
Log into the container with
docker execInstall software, create files, configure things
Exit the container
Create a new image from this configured container
docker commit <container_name> <new_image_name>
For example:
docker commit my-ubuntu-container my-custom-image
Run docker images afterward and you will see my-custom-image listed. From this new image, you can create fresh containers that already have all the software you installed.
Important caveat: docker commit is not the recommended way to build images in real-world teams. It is manual, not reproducible, and hard to audit. The proper way is to write a Dockerfile, which we will cover thoroughly in the next post. But docker commit is useful to know for quick experiments and emergencies.
The Complete Command Reference
Here is a clean reference of everything covered in this post:
Installation and Setup
yum install docker -y # Install Docker on Amazon Linux / RHEL
systemctl start docker # Start the Docker daemon
docker --version # Check Docker version
Image Commands
docker images # List local images
docker pull <image_name> # Pull an image from Docker Hub
docker rmi <image_id> # Remove an image
docker image prune # Remove all unused images
docker save <image> > file.tar # Export image to tar file
docker load -i file.tar # Import image from tar file
docker commit <container> <image_name> # Create image from container
Container Creation
docker run -it --name <name> <image> # Create and log into container
docker run -itd --name <name> <image> # Create container in background
Container Access
docker exec -it <name> /bin/bash # Log into running container (preferred)
docker attach <name> # Attach to container's main process
# Ctrl+P+Q # Exit container without stopping it
Container Listing
docker ps # List running containers
docker ps -a # List all containers
Container Lifecycle
docker stop <name> # Gracefully stop a container
docker kill <name> # Forcefully stop a container
docker start <name> # Start a stopped container
docker restart <name> # Restart a container
docker pause <name> # Pause container processes
docker unpause <name> # Resume paused container
Container Removal
docker rm <name> # Remove a stopped container
docker rm -f <name> # Force remove a running container
Container Inspection
docker inspect <name> # Full container details
docker stats <name> # Live CPU and memory usage
docker top <name> # Running processes inside container
docker logs <name> # Container output logs
File Operations
docker cp <file> <container>:<path> # Copy file to container
Summary
Docker's architecture is built around three components: the CLI, the REST API, and the Daemon. Always make sure the daemon is running before you start working.
Images are templates. Containers are running instances of those templates. Creating a container takes less than a second compared to several minutes for launching a virtual machine, and that speed and efficiency is exactly why containers have taken over.
The two most important behavioral differences to remember are:
Use
docker execoverdocker attachto log into containers safelyUse
Ctrl+P+QoverCtrl+Dto exit containers without stopping them
Every other command in this post is about managing the lifecycle of containers from creation through to removal. The more you practice these commands, the more natural they become. In the next post, we will move up a level and start building our own custom Docker images using Dockerfiles.






