Docker Fundamentals: Images, Containers and Volumes

DevOps 9 min readPublished 25 August 2026

Quick answer

Learn Docker images, containers and volumes through practical commands and a simple web server lab. Understand lifecycle management, persistence and common errors.

Docker packages an application with its runtime dependencies so that it can run consistently across development, testing and production environments. These Docker fundamentals are essential for beginners working with DevOps tools, cloud platforms and container-based deployment workflows.

If you want to practise Docker as part of a broader automation workflow, the AWS DevOps course covers related skills such as source control, CI/CD, cloud services and infrastructure operations.

What are the core Docker fundamentals?

The three core Docker objects are images, containers and volumes. An image is a reusable application package, a container is a running or stopped instance of that image, and a volume stores data outside the container's writable layer.

Docker objectPurposeExample
ImagePackages an application and its dependenciesnginx:alpine
ContainerRuns an isolated process from an imageAn Nginx web server
VolumePreserves application dataWebsite files or database data
RegistryStores and distributes imagesDocker Hub or Amazon ECR
DockerfileDefines how to build an imageInstructions using FROM, COPY and RUN

A useful diagram-in-words is:

Dockerfile -> docker build -> Image -> docker run -> Container
                                      |
                                      +-> Volume for persistent data

The image provides the starting filesystem and configuration. Docker adds a writable container layer when the image is started, but that layer is normally removed when the container is deleted.

How does Docker work?

Docker uses a client-and-engine architecture. The docker command sends requests to the Docker Engine, which manages images, containers, networks and volumes on the host.

User
  |
  v
Docker CLI -> Docker Engine -> Images
                            -> Containers
                            -> Networks
                            -> Volumes

On Linux, containers share the host kernel while using kernel features such as namespaces and control groups for isolation and resource management. Docker Desktop runs Linux containers through a managed Linux virtual machine on Windows and macOS.

Containers are not complete virtual machines. They usually start faster and use fewer resources because each container does not require a separate guest operating system.

FeatureContainerVirtual machine
IsolationProcess and filesystem isolationHardware-level virtualisation
Operating systemShares the host kernelIncludes a guest OS
Typical startupStarts as a processBoots an operating system
Packaging unitContainer imageVM disk image
Common useServices, jobs and development environmentsFull OS isolation and legacy workloads

How can you verify a Docker installation?

Docker is ready when the client can communicate with the Docker Engine and run a test container. Install Docker Engine or Docker Desktop using the official instructions for your operating system, then verify both the client and server.

docker version
docker info
docker run --rm hello-world

docker version displays client and server information. If it shows only client details or reports that it cannot connect to the daemon, the Docker Engine may not be running or the current user may not have access to its socket.

The --rm option automatically deletes the test container after it exits. Docker may first download the hello-world image if it is not available locally.

What is a Docker image?

A Docker image is a read-only template containing application files, libraries, runtime components and default configuration. Images are built in layers, which allows Docker to reuse unchanged layers across builds and reduce unnecessary transfers.

For example, nginx:alpine contains the Nginx web server on an Alpine Linux base. The part before the colon is the repository name, while alpine is the tag.

docker pull nginx:alpine
docker image ls nginx

Example output:

REPOSITORY   TAG      IMAGE ID       CREATED        SIZE
nginx        alpine   <image-id>     <time>         <size>

Tags are convenient labels, but a tag can be moved to a newer image. Production workflows can use an image digest when an exact immutable image version is required.

Build a simple image with a Dockerfile

Create a new directory and add an HTML page:

mkdir docker-web-lab
cd docker-web-lab

cat > index.html <<'EOF'
<!doctype html>
<html>
  <body>
    <h1>Docker fundamentals lab</h1>
    <p>This page is served from a custom image.</p>
  </body>
</html>
EOF

Create a file named Dockerfile:

FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80

Build the image from the current directory:

docker build -t beginner-web:1.0 .

The final dot is the build context. It tells Docker which directory contains the Dockerfile and files available to COPY instructions.

The instructions perform the following actions:

InstructionAction
FROM nginx:alpineSelects the base image
COPYAdds the local HTML file to the image
EXPOSE 80Documents the container's listening port

EXPOSE does not publish a port on the host. Port publishing happens when the container is started with -p or --publish.

Inspect the new image:

docker image ls beginner-web
docker history beginner-web:1.0

docker history shows the image layers and the instructions that created them. Docker can reuse cached layers when the Dockerfile and relevant files have not changed.

What is a Docker container?

A container is an isolated process created from an image. It receives a writable layer for runtime changes, but important data should not depend on that layer because deleting the container removes it.

Run the image built in the previous lab:

docker run -d --name demo-web -p 8080:80 beginner-web:1.0

The options mean:

OptionMeaning
-dRuns the container in detached mode
--name demo-webAssigns a readable container name
-p 8080:80Maps host port 8080 to container port 80
beginner-web:1.0Specifies the image and tag

Test the service:

curl http://localhost:8080

The request reaches port 8080 on the host. Docker forwards it to port 80 inside the container, where Nginx is listening.

Manage the container lifecycle

docker ps
docker ps -a
docker logs demo-web
docker stop demo-web
docker start demo-web
docker restart demo-web
docker rm -f demo-web

docker ps shows running containers, while docker ps -a also includes stopped containers. docker rm -f stops and removes a container, but it does not remove the source image or named volumes.

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

docker exec -it demo-web sh

The Nginx Alpine image provides sh, but it may not include Bash. Minimal images often omit diagnostic tools, so do not assume every image contains bash, curl or a package manager you normally use.

Why are Docker volumes required?

Docker volumes preserve data independently of a container's lifecycle. A container can be replaced or deleted while its named volume remains available for another container.

Consider a database container. If its database files exist only in the writable container layer, removing that container also removes the files. Attaching a volume places the files in storage managed separately by Docker.

Container writable layer -> Deleted with container
Named volume             -> Remains after container deletion

Docker supports several storage approaches:

Storage typeManaged bySuitable for
Named volumeDockerDatabases and persistent application data
Anonymous volumeDockerTemporary data without a fixed volume name
Bind mountUser and host filesystemSource code and host configuration files
tmpfs mountHost memory on LinuxSensitive or temporary non-persistent data

Create and use a named volume

Create a volume and write an HTML file into it:

docker volume create webdata

docker run --rm \
  --mount source=webdata,target=/data \
  alpine sh -c 'echo "<h1>Persistent Docker volume</h1>" > /data/index.html'

Start Nginx with the volume mounted read-only:

docker run -d \
  --name volume-web \
  -p 8081:80 \
  --mount source=webdata,target=/usr/share/nginx/html,readonly \
  nginx:alpine

curl http://localhost:8081

Remove the container and create a replacement:

docker rm -f volume-web

docker run -d \
  --name replacement-web \
  -p 8081:80 \
  --mount source=webdata,target=/usr/share/nginx/html,readonly \
  nginx:alpine

curl http://localhost:8081

The page still exists because webdata survived the deletion of the first container.

Inspect and remove volumes with:

docker volume ls
docker volume inspect webdata
docker volume rm webdata

Docker will reject the removal if a container still references the volume. Remove the relevant container before deleting the volume.

When should you use a bind mount?

Use a bind mount when a container needs direct access to a specific host directory, such as application source code during local development. Changes on either side are immediately visible because the container is accessing the host path.

On Linux or macOS, the current directory can be mounted as follows:

docker run --rm \
  --mount type=bind,source="$(pwd)",target=/workspace \
  alpine ls -la /workspace

Bind mounts depend on host paths and permissions, making them less portable than named volumes. Docker Desktop users may also need to permit file sharing for the selected host directory.

How do you troubleshoot common Docker errors?

Start troubleshooting by checking container status, logs, port mappings and mounts. Avoid repeatedly rebuilding an image until you know whether the failure is occurring during the build, container startup or application runtime.

Cannot connect to the Docker daemon

Typical error:

Cannot connect to the Docker daemon

Check whether Docker Engine or Docker Desktop is running:

docker version
docker info

On Linux, socket permissions may also be responsible. Use the Docker installation guidance for your distribution before changing group membership, and understand that access to the Docker socket provides powerful host-level control.

A container exits immediately

Containers run only while their main process is active. Check all containers and inspect the logs and exit code:

docker ps -a
docker logs demo-web
docker inspect --format '{{.State.Status}} exit={{.State.ExitCode}}' demo-web

An exit code of zero usually indicates normal completion. A non-zero code suggests an application, command, configuration or permission failure.

The host port is already in use

Typical error:

Bind for 0.0.0.0:8080 failed: port is already allocated

Find the existing published ports:

docker ps --format 'table {{.Names}}\t{{.Ports}}'

Stop the conflicting service or choose another host port:

docker run -d --name demo-web-2 -p 8082:80 beginner-web:1.0

Only the host-side port changed. Nginx still listens on port 80 inside the container.

Mounted files are missing

A mount placed over a non-empty container directory hides the original files at that path. Inspect the mounts before concluding that the image build failed:

docker inspect --format '{{json .Mounts}}' replacement-web

Check whether the source volume contains the expected data:

docker run --rm --mount source=webdata,target=/data alpine ls -la /data

The container receives permission denied errors

The process inside the container may run as a non-root user whose UID and GID cannot access the mounted files. Check the image's runtime user, file ownership and host security controls rather than automatically running the application as root.

docker exec replacement-web id
docker exec replacement-web ls -ld /usr/share/nginx/html

For production images, set appropriate ownership during the build and use the least-privileged user that can run the application.

What Docker practices should beginners follow?

Use explicit image tags, keep images small, store persistent data in volumes and remove unused lab resources carefully. Treat images as replaceable build artefacts rather than servers that are manually modified after deployment.

Practical habits include:

  • Write application changes in the Dockerfile instead of editing running containers.
  • Use .dockerignore to exclude Git data, credentials, logs and unnecessary build files.
  • Never place passwords, access keys or private keys inside an image.
  • Run applications as a non-root user where the image supports it.
  • Publish only the ports required by the application.
  • Use read-only mounts when a container does not need to change mounted files.
  • Review base images and rebuild when security updates are available.
  • Remove unused test containers with docker container prune only after reviewing what will be deleted.

Cloud learners can connect these concepts to image storage in Amazon Elastic Container Registry and container execution services such as Amazon ECS. The AWS Solutions Architect course provides broader context for selecting compute, networking and storage services, while these AWS DevOps interview questions can help you review operational scenarios after completing the labs.

What should you remember about Docker fundamentals?

An image is the reusable package, a container is an instance of that package, and a volume provides persistent storage outside the container layer. Build images with Dockerfiles, publish ports deliberately, inspect logs before troubleshooting blindly, and keep important state in suitable external storage.

The best way to learn these concepts is to repeat the image build, container replacement and volume persistence labs until the object lifecycle is clear.

Reviewed by Network Rhinos DevOps trainers.

To practise Docker with cloud services, CI/CD tools and deployment workflows, enquire about batch details for the AWS DevOps course.

Related reading: AWS IAM Explained: Users, Roles, Policies and Access

Frequently asked questions

What is the difference between a Docker image and a container?

A Docker image is a read-only package containing application files and dependencies. A container is a running or stopped instance created from that image, with an additional writable layer for runtime changes.

Does deleting a Docker container delete its image?

No. Deleting a container removes that container and its writable layer, but the source image remains available. You can use the same image to create another container.

Do Docker volumes remain after a container is removed?

Named volumes normally remain after a container is removed. They must be deleted separately with `docker volume rm` or an appropriate prune command.

What is the difference between a Docker volume and a bind mount?

A named volume is managed by Docker and is generally suitable for persistent application data. A bind mount maps a specific host path into a container, making it useful for source code and local configuration during development.

Why does a Docker container exit immediately?

A container exits when its main process finishes or fails. Use `docker ps -a`, `docker logs <container>` and `docker inspect <container>` to check its status, output and exit code.

Does EXPOSE publish a Docker container port?

No. `EXPOSE` documents the port expected by the application, but it does not create a host mapping. Use an option such as `-p 8080:80` when starting the container to publish the port.

Related articles

Train with Network Rhinos

Hands-on CCNA, CCNP, AWS, Azure, DevOps and cybersecurity training in Chennai & Bangalore, with placement support. Talk to our team or attend a free demo class.