Sitemap
Samuel McLamb

Samuel McLamb

Cybersecurity/Computer Science & Rachmaninoff enthusiast. Rachmaninoff's goated.

Docker notes

9 min readDec 17, 2025

--

This article will cover the basics of containers, why we use containers, docker, why docker, and how it works on a software level.

Press enter or click to view image in full size
`Containers

Containerization in software deployment is the process of bundling an application’s code, dependencies, or whatever else it needs to run reliably, on an isolated environment called a container. This process make it much easier and hassle-free to run and test on different machines.

Most modern production applications are very complex and often may incorporate multiple frameworks and libraries to be installed in specific ways to run.

Developers and software testers may run into problems installing dependency software on their test computers that may not be supported, developers might have trouble replicating and fixing issues if the problem is in the environment not the code itself, decencies might conflict with each other, for example code that relies on two different glibc versions might not work if one of those versions is different even if the other one is correct.

Container Visualization

This diagram represents a deployment application with various DevOps applications such as nginx, MongoDB, Logspout, ELK, and Graphite all running on a container (in this case docker).

The actual isolation of these containers are done using the “namespace” feature of the Linux kernel, which allows processes access to resources of the operating system without being able to see or communicate with other processes. This adds to security because if a properly configured container is compromised, the entire machine isn’t compromised.

When it comes to containerization software the undeniable king is docker

What is Docker?

“Docker helps developers build, share, run, and verify applications anywhere — without tedious environment configuration or management.” — docker.com

Docker works on Linux, Windows, and MacOS. Applications are called “images” and are shared with others. As long as the right version of docker is installed it’s entirely possible to simply pull down the correct image and run it with docker.

The application that manages all these images is known as the “Docker Engine”, this application is essentially an API that responds to requests from docker containers. The Docker engine is full of features such as, container connection, import and export applications (images), file transfer between the host operating system and the container.

Some interesting usage facts about Docker:

  • 13 million developers are using Docker [1]
  • There are 7 million applications made and ready to use with Docker [2]
  • 13 billion applications are downloaded monthly! [3]
  • …and this is just from the official repository

[1, 2]. Dockerhub.com 04/2022

[3]. Docker.com 04/2022

Here are some of the most notable features of docker from the lens of software deployment efficiency in mind.

  • Docker is Free
  • Docker is Compatible (works on Linux, MacOS, and Windows)
  • Docker is Efficient & Minimal (Much more efficient for it’s job than virtual machines)
  • Docker is easy and simple — it’s very well documented
  • Docker is easy to share with others

How does the containerization work on a high level?

Docker uses both cgroups and namespaces. The creation of the namespace isolates what the docker container can see, the container runs in a namespace where it can only see it’s own processes, virtual network stack, filesystem, hostname and network connections. Control groups (cgroups) are what separate system resources such as CPU, memory, and network bandwidth.

The syntax for Docker can be categorized into these four groups:

  • Running a container
  • Managing & Inspecting containers
  • Managing Docker images
  • Docker daemon stats and information

To run a container you need an image. This is what the

docker pull <image>

command is for

For example:

docker pull nginx

would pull down the docker nginx image.

The

docker image

command with the appropriate option, allows us to manage the image on our local system

The most fundamental and essential commands are

  • pull (we already covered this)
  • ls (list images)
  • rm (remove an image)
  • build (a command essential to building an image)

Here is the commands output

Each pull complete represents a layer being added to the image

Using the docker image ls command we can see what images we have installed on the system:

The output from the docker image rm command is

Quick tangent!

What are Docker layers?

Consider the following Dockerfile:

FROM node:10-alpine 
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app
COPY package *. json ./
USER node
RUN npm install
COPY -chown=node:node ..
EXPOSE 8080
CMD ["node", "app.js" ]

Each of these instructions that modifies the file system will generate a new layer. Since each of these instructions modifies the filesystem in some way, they each add a layer. Anyways back to nginx:

We can see it’s removing all the layers

It’s also worth noting that it’s possible to download an older version of software by using a tag such as:

The same goes for removing them as well.

Get Samuel McLamb’s stories in your inbox

Join Medium for free to get updates from this writer.

For running containers we can use the following syntax:

A common example for showcasing how to run docker containers looks like the following:

The -it flag means interactive and the /bin/bash represents the command to be run inside of the container. This gives us a shell inside our Ubuntu container.

Press enter or click to view image in full size

Here’s a dissection of every common docker run option:

  • -a This option is used for attaching some kind of file stream such as stdin stdout or stderr, it’s useful for situations where you only want one steam of data to or from the container. For example there might be a situation where the standard output doesn’t matter and all you want to see is the standard error. docker run -a stderr <container image>
  • -c This option allows you to set CPU priority when running containers
  • -d This option basically means the docker container is running in the background
  • -e This option allows you to set environment variables in the container
  • -h This option allows you to set the hostname in the container
  • -i This option means interactive, meaning stdin and stdout are both attached.
  • -l This option allows you to set metadata on the container
  • -m This options hard-codes a memory usage limit for the container.
  • -v This argument is short for “Volume” and tells Docker to mount a directory or file from the host operating system to a location within the container. The location these files get stored is defined in the Dockerfile
  • -p, This option tells docker to bind teh container on the host operating system to a specific port.
  • -rm This option tells docker to remove the container once it finishes running,
  • — name This option tells docker to give the container a name of your chosing. (note there are two dashes there not one, medium just combined them)

It’s possible to list running containers with the

docker ps

command.

Press enter or click to view image in full size

The output here will give us lots of good information on the status of a container.

Prebuilt images are nice but what if the image we want to create relies on a custom application that isn’t listed on the docker hub?

This is where Dockerfiles come into the picture. Dockerfiles are simply text files that service as an instruction manual for what containers should do.

The syntax for a instruction is:

INSTRUCTION argument

Here is a compiled list of the most necessary docker instructions:

  • FROM This instruction will set new build stage and act as the base image for any subsequent actions — This is typically an operating system.
  • RUN This instruction will execute commands in the container within a new layer.
  • COPY This instruction can copy files to or from the container image.
  • WORKDIR This is the command that sets the entry point directory on the image.
  • CMD This is the instruction is what tells the container the what command should be run on startup
  • EXPOSE This instruction serves as documentation for anyone reading to docker file to know what port the container will run on.

Dockerfiles must have the exact name of “Dockerfile” for the docker engine to know where the configuration file is without additional configurations.

What’s the difference between CMD and RUN (and other one: ENTRYPOINT)?

The CMD instruction specifies the default command to run when a container is started from the Docker image. If no command is specified during the container startup (i.e., in the docker run command), this default is used. CMD can be overridden by supplying command-line arguments to docker run

The RUN instruction is used in Dockerfiles to execute commands that build and configure the Docker image. These commands are executed during the image build process, and each RUN instruction creates a new layer in the Docker image. For example, if you create an image that requires specific software or libraries installed, you would use RUN to execute the necessary installation commands.

The dockerfile we will use will look like the following:

# Use Ubuntu 22.04 as the base operating system of the container
FROM ubuntu:22.04

# Set the working directory to the root of the container
WORKDIR /

# Create helloworld.txt
RUN touch helloworld.txt

The command to run this helloworld container would be:

docker build -t helloworld .

Press enter or click to view image in full size

We can then use docker image ls to see if the image has been built

We can also see from the build output that whatever image was listed in the FROM instruction will also be downloaded.

This hello world example wasn’t very practical though. We can do better.

Take this Dockerfile

FROM ubuntu:22.04

# Update the APT repository to ensure we get the latest version of apache2
RUN apt-get update -y

# Install apache2
RUN apt-get install apache2 -y

# Tell the container to expose port 80 to allow us to connect to the web server
EXPOSE 80

# Tell the container to run the apache2 service
CMD ["apache2ctl", "-D","FOREGROUND"]

This one is an example web server using apache2. Here is the command for it:

docker run -d --name webserver -p 80:80 webserver

  • d runs it in the backgroud
  • — name gives the container a name
  • -p tells the host and container which ports to expose with the syntax host:container
  • and finally the image to run

The full syntax of run is:

Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG…]

We can also use the ubuntu:22.04-minimal to make the image even smaller.

What is Docker Compose?

There is a problem with docker as it is so far in these notes. Applications that contain multiple services, for example there might be a web application with one server for database, and another for serving content. This isn’t scalable with a single docker container. This is where Docker compose comes in, it’s built off of the docker engine and it allows for seamless, scalable, and efficient deployment of web applications.

I believe to truly showcase the usefulness of something you must first clearly define the problem, and the problem here that compose solves is inefficiency, disorganization, and unnecessary confusion.

The following application runs a base-bones student management system. The following diagram depicts the structure:

The following demo is taken from this repository:

https://github.com/Mrinfallable/compose-is-useful

The the compose.yml file in with_compose here

version: "3.9"

services:
database:
build: ./database
container_name: database
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
ports:
- "3306:3306"

frontend:
build: ./frontend
container_name: frontend
depends_on:
- database
ports:
- "80:80"
command: sh -c "sleep 13 && python3 app.py"

processing:
build: ./processing
container_name: processing
depends_on:
- database
command: sh -c "sleep 6 && python3 processor.py"

Provides several useful features compared to the deploy.sh file here

#!/bin/bash
docker build -t production ./production
docker build -t processing ./frontend
docker build -t database ./database
docker network create --subnet 172.20.0.0/16 --gateway 172.20.0.1 app-network

#commands to start the services:
docker run -d --network app-network --ip 172.20.0.3 -p 3306:3306 -d --name database database
sleep 13
docker run -d --network app-network --ip 172.20.0.2 -p 80:80 -d --name frontend frontend
sleep 1
docker run -d --network app-network --ip 172.20.0.4 -d --name processing processing

such as

  1. The network never needs to be manually created
  2. Docker (with compose) in this example provides build-in DNS
  3. Streamlined for other DevOps services such as Kubernetes, Azure DevOps, and AWS.

--

--

Samuel McLamb
Samuel McLamb

Written by Samuel McLamb

Cybersecurity/Computer Science & Rachmaninoff enthusiast. Rachmaninoff's goated.