Skip to content

Docker For New Users

Note

These guidelines apply to Docker in general, but are written with the Computer Science shared servers in mind.

Warning

Docker typically requires either root privileges or membership in the docker group.
Users in the docker group effectively gain root-level access to the host system because containers can access many system resources.

For this reason, Docker access on shared servers is restricted and should only be used when necessary for coursework or research.

Basic concepts

Before using Docker, it is useful to understand the four core concepts of Docker.

Dockerfile
A Dockerfile is a text file containing instructions that is used when building Docker images.

Image
A Docker image is a packaged filesystem that contains an application and its dependencies.

Container
A container is a running instance of an image. Containers run processes that are isolated from the host system with their own filesystem, networking stack, and process tree.

Volume / Mount
A volume or mount allows files to persist outside the container so that data is not lost when the container stops.

Each of these concepts is covered in more detail below.


Dockerfiles

What is a Dockerfile?

A Dockerfile is a text file that contains a set of instructions used to build a Docker image.
Each instruction defines a step in the image creation process.

When docker build is executed, Docker reads the Dockerfile from top to bottom and creates a layer for each instruction.

Basic structure of a Dockerfile

A Dockerfile consists of instructions that define:

  • the base environment
  • required dependencies
  • application code
  • the command that runs when the container starts

Example:

FROM python:3.12

WORKDIR /app

COPY requirements.txt .

RUN pip install -r requirements.txt

COPY . .

CMD ["python", "app.py"]

Each of the Docker instructions is covered in more detail below.


Common Dockerfile Instructions

FROM

Defines the base image used to build the new image.

Every Dockerfile must start with a FROM instruction (except when using scratch).

Example:

FROM ubuntu:24.04

or

FROM python:3.12

The base image provides the operating system and runtime environment.


WORKDIR

Sets the working directory inside the container.

Example:

WORKDIR /app

All subsequent commands will run from this directory.

If the directory does not exist, Docker creates it automatically.


COPY

Copies files from the build context on the host into the image.

Example:

COPY requirements.txt .

This copies requirements.txt from the host directory into the current container working directory.

Copy an entire directory:

COPY src/ /app/src/

ADD

ADD is similar to COPY, but with additional features:

  • can extract .tar archives
  • can download remote URLs

Example:

ADD archive.tar.gz /data/

For most situations, COPY is preferred because its behaviour is simpler and more predictable.


RUN

Executes a command during image build.

Example:

RUN apt-get update && apt-get install -y curl

This installs packages into the image.

Each RUN instruction creates a new layer.

To reduce the number of layers, related commands are often chained together.

Example:

RUN apt-get update \
 && apt-get install -y curl git \
 && rm -rf /var/lib/apt/lists/*

ENV

Defines environment variables inside the container.

Example:

ENV APP_ENV=production

These variables are available when the container runs.


EXPOSE

Documents which ports the container expects to use.

Example:

EXPOSE 8080

This does not publish the port automatically; it only provides metadata.

Ports must still be mapped using:

docker run -p 8080:8080 IMAGE

CMD

Defines the default command executed when a container starts.

Example:

CMD ["python", "app.py"]

If a command is provided in docker run, it overrides the CMD.

Example:

docker run IMAGE python test.py

ENTRYPOINT

Defines the main executable for the container.

Example:

ENTRYPOINT ["python"]

In this case:

docker run IMAGE script.py

would execute:

python script.py

ENTRYPOINT is often used when a container should always run a specific program.


USER

Specifies which user runs commands inside the container.

Example:

USER appuser

Running containers as non-root users improves security.


Dockerfile build layers

Each Dockerfile instruction creates a layer in the image.

Example:

FROM ubuntu
RUN apt install python
COPY app.py

Layers are cached by Docker.

If a layer has not changed, Docker reuses the cached layer instead of rebuilding it. This significantly speeds up builds.

However, if an earlier layer changes, all subsequent layers must be rebuilt.


Minimising image size

Images should be kept as small as possible.

Common techniques:

Use minimal base images

Example:

alpine
python:3.12-slim

Combine commands

Instead of:

RUN apt update
RUN apt install -y curl

Use:

RUN apt update && apt install -y curl

Remove unnecessary files

Example:

RUN apt-get update \
 && apt-get install -y curl \
 && rm -rf /var/lib/apt/lists/*

.dockerignore

A .dockerignore file prevents unnecessary files from being included in the build context.

Example:

.git
node_modules
__pycache__
*.log

This improves build performance and reduces image size.


Multi-stage builds

Multi-stage builds allow multiple build environments to be used within a single Dockerfile.

This is commonly used when:

  • compiling software
  • building frontend applications
  • producing minimal runtime images

Example:

FROM node:20 AS builder

WORKDIR /build
COPY package.json .
RUN npm install
COPY . .
RUN npm run build

FROM nginx:latest

COPY --from=builder /build/dist /usr/share/nginx/html

In this example:

  1. The first stage builds the application.
  2. The second stage copies only the compiled output into a smaller runtime image.

This avoids including build tools in the final container.


Dockerfile practices for shared servers

On shared infrastructure:

  • build images with meaningful names
  • avoid extremely large images
  • remove temporary build artifacts
  • avoid unnecessary background services inside containers

Example naming convention:

<username>_<application>:<version>

Docker Images

What is an Image?

A Docker image is a read-only template that contains an application and everything required to run it, including:

  • the application code
  • system libraries
  • runtime dependencies
  • configuration files

Images are used to create containers. Multiple containers can be started from the same image.

Images are typically obtained in one of two ways:

  1. Pulling an image from a registry

    docker pull python:3.12
    

    This downloads the image from a registry such as Docker Hub1.

  2. Building an image locally

    Images can be created from a Dockerfile:

    docker build --tag myimage:v1 .
    

    Where:

    • --tag assigns a name and optional version tag
    • . is the build context (usually the directory containing the Dockerfile)

Listing images:

docker images

Each image is identified by:

  • repository name
  • tag
  • image ID

Example output:

REPOSITORY        TAG       IMAGE ID
python            3.12      abc12345
myimage           v1        def67890

Docker Containers

What is a Container?

A container is a running instance of a Docker image.

When a container starts:

  1. Docker creates a writable layer on top of the image
  2. The container runs a process inside an isolated environment
  3. When the process exits, the container stops

Basic container execution:

docker run <IMAGE> <COMMAND>

Example:

docker run python:3.12 python --version

Common docker run options

Option Purpose
--name Assign a name to the container
-it Interactive terminal
-d Run container in background
-p Map ports between host and container
--rm Automatically remove container when it stops
-e Set environment variables
-v / --mount Attach storage volumes

Example interactive container:

docker run -it ubuntu bash

Example background service:

docker run -d nginx

Temporary containers

Containers accumulate quickly if they are not removed after execution.

For short-lived containers use:

docker run --rm <IMAGE>

Example:

docker run --rm alpine echo "Hello"

This removes the container immediately after it stops.

Advantages:

  • prevents buildup of unused containers
  • encourages correct use of persistent storage

Limitation:

  • logs are unavailable after container removal.

Docker Volumes

What are Volumes?

Containers are ephemeral by default. Data written inside a container may be lost when the container is removed.

Volumes provide persistent storage that exists outside the container lifecycle.

Volume Types

Two common volume types exist:

  1. Bind mounts

    A host directory is mounted inside the container.

    Example:

    docker run --mount type=bind,source=/home/user/data,target=/data <IMAGE>
    

    Files written to /data inside the container appear directly in /home/user/data on the host.

    Bind mounts are useful when:

    • working with local source code
    • sharing files between host and container

  1. Named volumes

Docker-managed volumes are created and managed by Docker itself or can be manually created, which is covered after this section.

Example:

docker run --mount source=myvolume,target=/data <IMAGE>

Volumes persist even if containers are removed.

List volumes:

docker volume ls

Inspect a volume:

docker volume inspect myvolume

Creating volumes

Volumes can be created either automatically by Docker or manually by the user.

Automatic volume creation

If a container references a volume that does not yet exist, Docker will create it automatically.

Example:

docker run --mount source=myvolume,target=/data <IMAGE>

If myvolume does not exist, Docker will create it before starting the container.


Manual volume creation

Volumes can also be created explicitly using the Docker CLI:

docker volume create <VOLUME_NAME>

This creates a persistent volume managed by Docker.

You can verify that the volume exists with:

docker volume ls

Inspect detailed information about a volume:

docker volume inspect <VOLUME_NAME>

Once created, the volume can be attached to containers using docker run --mount or through Docker Compose.


Naming conventions on shared servers

It is very important to keep things organised on a shared server resource, especially with Docker. That is because it is not simple to tell who ran or built what with Docker. Most processes related to docker look like the root user ran them.

Therefore, all images and containers and volumes should be named something that includes the users SU number. You should note the following implications when not naming your images and/or containers.

  • All images and containers not named correctly may be removed in order to free up space if needs be, before properly named images and containers are considered.
  • If a docker container is using up an unreasonable2 percentage of resources, and it has not been named in an intuitive way, the system admin will not know who to contact in order to follow up. As such, you will not only incur the ire of said system admin, but your container may be stopped before the work is finished.

Cleaning up Docker resources

Over time Docker accumulates:

  • stopped containers
  • unused images
  • unused volumes
  • unused networks

Useful inspection commands:

docker ps -a
docker images
docker volume ls
docker network ls

Manual cleanup examples:

Remove a container:

docker rm <CONTAINER>

Remove an image:

docker rmi <IMAGE>

Remove a volume:

docker volume rm <VOLUME>

Warning

Commands such as:

```bash
docker image prune
docker container prune
docker volume prune
docker system prune
```

operate **system-wide** and may remove objects created by other users.

These commands should **not be used on shared servers** unless instructed by an administrator.

Docker Compose

When applications require multiple containers, managing them manually with docker run becomes difficult.

Examples:

  • web application
  • database server
  • caching layer
  • message queue

Docker Compose allows these multi-container systems to be defined in a single configuration file.

Compose uses a YAML file named:

docker-compose.yml

Each container is defined as a service.

Example:

version: "3.9"

services:
  web:
    image: nginx
    ports:
      - "8080:80"

  db:
    image: postgres
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

Key Compose concepts:

Services

A service defines a container configuration such as:

  • image
  • ports
  • environment variables
  • mounted volumes

Networks

Compose automatically creates a network allowing services to communicate using their service names.

Shared volumes

Volumes declared in the Compose file allow containers to persist and share data.


Running a Compose application

Start all services:

docker compose up

Start in the background:

docker compose up -d

Stop services:

docker compose down

This will stop and remove containers created by the Compose project.

Compose simplifies:

  • starting multi-container systems
  • managing container dependencies
  • sharing configuration across environments

Official documentation:

https://docs.docker.com/compose/


  1. https://hub.docker.com/ 

  2. In this context, "unreasonable" implies something is likely wrong with the container: storage or RAM may be filling up, or the CPU/GPU maybe running at high load for a long time. 


Last update: 2026-03-16
Created: 2026-03-16