Basics

Examples using public docker images

You can get the list of public docker images here: https://hub.docker.com/search

Very simple example running a specific python console from a docker image:

docker run -it --rm python:3.13-bookworm python

Note

-it is used to map a port from the host machine to a port inside the Docker container. -rm is used to automatically remove the container when it exits.

Below is a simple example using basics docker features to start a postgresql database server. A pgadmin docker image is also used to connect to the database.

Creating/Running the database server:

docker run -d \
    --name postgres-container-name \
    -p 5432:5432 \
    -e POSTGRES_USER=mycustomuser \
    -e POSTGRES_PASSWORD=mysecretpassword \
    -e POSTGRES_DB=mycustomdatabase \
    -e PGDATA=/var/lib/postgresql/data/pgdata \
    -v /custom/mount:/var/lib/postgresql/data \
    postgres

Note

-p is used to map a port from the host machine to a port inside the Docker container.

Here, it allows us to access the database from HOST using the URL localhost:5432.

-e is used to set environment variables inside a Docker container.

Here, we use it to setup database configuration such as user, password, database name and the path where to store the database data.

-v is used to mount a volume or a bind mount between the host machine and the Docker container.

Here, we use it to keep the database data (persistent data) even when the container is stopped or deleted.

Note

Docker will pull the image automatically if it doesn’t exist locally.

Note

Container run by default in bridge network mode. You can run it in host by using the --network host argument.

If you have psql installed on your HOST machine, you can use the following command to access the database:

psql -h localhost -U mycustomuser -d mycustomdatabase

Runing a pgadmin docker image to access the database:

docker run \
    --network host \
    -e 'PGADMIN_DEFAULT_EMAIL=user@domain.com' \
    -e 'PGADMIN_DEFAULT_PASSWORD=SuperSecret' \
    -d dpage/pgadmin4

You can now access pgadmin in your browser using the URL: http://localhost:80, enter the fake email/password and add a new server using host localhost and port 5432 and the database credentials set when running the postgres container.

Note

We started pgadmin in host network mode in order to access the database using localhost, if you prefer to start it in bridge mode, you’ll need to map port 80 when running the docker pgadmin container, you’ll also need to enter the postgres container IP adress instead of localhost (see the command below to get the IP adress of a running docker container)

Dockerfile: Build your custom image

Let’s consider a python project with the following structure:

.
├── Dockerfile
└── print_and_save_message.py

With print_and_save_message.py content being:

import argparse
import os

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Print Something")
    parser.add_argument("message", type=str, help="Message to print")
    parser.add_argument("--output", type=str, default=".", help="Path where to save the message")
    args = parser.parse_args()
    print(f"MESSAGE: {args.message}")
    print(f"EXTRA_MESSAGE: {os.environ.get('EXTRA_MESSAGE')}")
    print(f"PATH: {args.output}")
    with open(os.path.join(args.output, "message.txt"), "w") as f:
        f.write(args.message)

Below is the Dockerfile that demonstrates the functionality of key features, including:

  • The use of basic key instructions: ARG, FROM, RUN, ENV, COPY, WORKDIR, CMD, ENTRYPOINT

  • Multi stage builds

  • Executing command as a non-root user

ARG PYTHON_IMG="python:3.13-bookworm"
ARG USER_UID=2000
ARG USER_GID=2000
# BUILD STAGE
FROM ${PYTHON_IMG} AS builder
RUN mkdir /project
COPY print_and_save_message.py /project/print_and_save_message.py

# RUN STAGE
FROM ${PYTHON_IMG}
ARG USER_UID
ARG USER_GID
COPY --from=builder /project /project
RUN groupadd -g ${USER_GID} newuser
RUN useradd newuser -u ${USER_UID} -g ${USER_GID} -m -s /bin/bash
USER newuser
ENV EXTRA_MESSAGE="Welcome"
WORKDIR /project
ENTRYPOINT ["python", "print_and_save_message.py"]
CMD ["Hello World!"]

There are multiple important aspects to understand in this Dockerfile:

  • ARG is available at build time only, and the default value can be overwrite by the --build-arg VAR=value

  • When using ARG globally (before any FROM instruction) in multiple stages as in our case, we need to “renew” the ARG at each stage.

  • We create a user with the possibility to set explicitly the user UID and GID during the build command. Doing so, if the UID/GID match the UID/GID of the HOST user and if we bind a volume inside the container, files generated by the script in the container will be created as if it was created by the HOST.

You can use the following command to build the image:

docker build --build-arg PYTHON_IMG="python:3.12-bookworm" --build-arg USER_UID=$(id -u) --build-arg USER_GID=$(id -g) -t test-img .

And run the image using:

mkdir container_output
docker run -it --rm -v ./container_output:/home/newuser test-img "Bye World" "--output" "/home/newuser"
# Alternative using --mount option
docker run -it --rm --mount type=bind,source=./container_output,target=/home/newuser test-img "Bye World" "--output" "/home/newuser"

Note

If you want the container to have only read access to the HOST volume, you can use arguments -v ./container_output:/home/newuser:ro or --mount type=bind,source=./container_output,target=/home/newuser,readonly.

Differences between ENTRYPOINT and CMD

The difference between ENTRYPOINT and CMD:

  • CMD: Specifies the default command and arguments to execute when running a container. It can be overridden by specifying a command in the docker run command. It Can be specified in three forms:
    • Shell form: CMD command param1 param2

    • Exec form: CMD [“executable”, “param1”, “param2”]

    • As default parameters to ENTRYPOINT: CMD [“param1”, “param2”]

  • ENTRYPOINT: Defines the executable that will always be run in the container. It is designed to not be overridden unless explicitly overridden with --entrypoint in the docker run command.
    • Typically specified in exec form: ENTRYPOINT [“executable”, “param1”, “param2”]

    • If combined with CMD, the CMD provides default arguments to the ENTRYPOINT

Create a user with the same UID/GID as host

ARG USER_UID
ARG USER_GID
RUN groupadd -g ${USER_GID} newuser
RUN useradd newuser -u ${USER_UID} -g ${USER_GID} -m -s /bin/bash
USER newuser
docker build --build-arg USER_UID=$(id -u) --build-arg USER_GID=$(id -g) -t image-name .

Useful commands

List the containers:

# Only running containers
docker ps
# All the containers
docker ps -a
# Custom formatting
docker ps --format "table {{.Image}}\t{{.Ports}}\t{{.Names}}\t{{.Mounts}}"

You can set the default formatting by editing the file ~/.docker/config.json, example for the docker ps command:

{
    "psFormat": "table {{.ID}}\\t{{.Image}}\\t{{.Status}}\\t{{.Names}}\t{{.Mounts}}"
}

Get a container IP adress:

docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id

Delete all containers:

docker container stop $(docker container ls -aq)
docker container prune -f # Only delete non-running containers

Delete all images:

# Containers using the image need to be removed first
docker rmi -f $(docker images -aq)

Docker volumes:

# Create a volume
docker volume create volume_name
# List the volumes
docker volume ls
# Inspect a volume (eg to get its mount point)
docker volume inspect volume_name
# Delete a volume
docker volume rm volume_name

Sources: