Skip to main content

Command Palette

Search for a command to run...

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.

Updated
14 min read
Docker Architecture, Installation, and Complete Container Lifecycle Management
S
Passionate about Cloud and DevOps engineering. I write structured technical notes and beginner-friendly articles on AWS, Linux, CI/CD, networking, system architecture and modern software delivery workflows.

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:

  1. Docker CLI (the client): This is you, typing commands in your terminal.

  2. REST API: The CLI does not talk directly to Docker's core. It sends HTTP requests through a REST API layer.

  3. 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:

  1. Pull them from Docker Hub (the public registry)

  2. 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:

  1. Pull a base image (ubuntu, for example)

  2. Create a container from it

  3. Log into the container with docker exec

  4. Install software, create files, configure things

  5. Exit the container

  6. 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 exec over docker attach to log into containers safely

  • Use Ctrl+P+Q over Ctrl+D to 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.

More from this blog

Ansible Playbooks Explained: From First YAML File to Managing Real Servers

If you have already run a few Ansible ad hoc commands and seen how they work, you already understand the core idea: one command, many servers. But ad hoc commands only take you so far. When you need to install software, start services, create users, copy files, and print confirmation messages, all in one automated run across multiple servers, that is when you move to playbooks. Playbooks are where Ansible truly earns its place in a DevOps workflow. Everything you do in Ansible at scale, you do through playbooks.

Jun 19, 202613 min read
Ansible Playbooks Explained: From First YAML File to Managing Real Servers

You Have 400 Servers to Configure. Now What? Let Ansible Do the Work.

Picture this. You have four EC2 instances running in your AWS account, and someone asks you to install Apache on all four of them. What do you do? The obvious answer most people go with is SSH into each machine, run the install command, repeat. Simple enough when it is four servers. But what happens when it is forty? Or four hundred? In a real enterprise environment, that number is not exaggerated at all. That is exactly the problem that Ansible was built to solve. And once you understand what it does and how it thinks, you will wonder how anyone managed large infrastructure without it.

Jun 18, 202612 min read
You Have 400 Servers to Configure. Now What? Let Ansible Do the Work.

Building Production-Ready Docker Deployments with Secrets, Stacks, and Distroless Images

This post wraps up the core Docker Swarm curriculum by covering four important topics that complete the picture of production-ready containerized deployments. We start with Docker Secrets, which solves the real-world problem of passing sensitive credentials into containers without hardcoding them. We then look at Docker Stack, which is how you run multi-service Docker Compose files across a Swarm cluster instead of a single host. After that we cover the distinction between replicated and global services, which is a concept that appears in Kubernetes as well. We close with a look at Portainer for those who prefer a visual interface, and a brief introduction to distroless images. Each of these topics builds on everything covered so far. If you have your Swarm cluster running, you can follow along with every command shown here.

Jun 17, 202614 min read
Building Production-Ready Docker Deployments with Secrets, Stacks, and Distroless Images

Beyond One Server: Solving Docker Scaling with Swarm and Container Networks

This post covers three separate but deeply connected topics. We start by finishing what was started with Docker Hub, pushing all four bank service images to a remote registry so they survive beyond any single machine. From there, we identify a real architectural problem with single-host Docker deployments and introduce Docker Swarm as the solution. Finally, we close with Docker networking, explaining how containers communicate with each other both on the same host and across different hosts. By the end of this article, you will understand how to push and pull images from Docker Hub, how to set up a multi-node Docker Swarm cluster, how to create and scale services across that cluster, what self-healing means in practice, and how Docker networking works under the hood.

Jun 17, 202618 min read
Beyond One Server: Solving Docker Scaling with Swarm and Container Networks

Docker Compose in Action: Multi-Container Apps, Nginx Load Balancing & Docker Hub

We built four containerized microservices for an bank application in the last post. Internet banking, mobile banking, insurance, and loans, each running in its own container, each exposed on a separate port. The setup worked. But the process of building and running each container individually by hand was repetitive, error-prone, and simply not practical at scale. This post introduces Docker Compose, and by the end, you will understand not just how to use it, but why it exists, what its real limitations are, how to combine it with Nginx to build a working high availability architecture, and how to push your images to Docker Hub so they are available beyond your local machine. There is also a hands-on project included here that builds a Flask-based Python application behind an Nginx load balancer, which you are expected to complete as a practical exercise.

Jun 16, 202617 min read
Docker Compose in Action: Multi-Container Apps, Nginx Load Balancing & Docker Hub
S

Sai Praneeth's Blogs

37 posts

From SDLC and Agile to DevOps and CI/CD, this blog is where I share structured technical notes, concepts and practical insights in Cloud and DevOps engineering.