Docker Tutorial for Beginners: How to Use Docker and Docker Compose

Computer code on a screen for a Docker tutorial for beginners

Docker becomes much easier once you separate four ideas: an image is a packaged application, a container is a running instance of that image, a registry stores images, and Docker Compose describes multiple containers in a YAML file. This guide walks through the basic Docker commands first, then shows how to run a small web service and database together with Compose.

Docker basics in one minute

  • Image: a read-only template containing an application and its dependencies.
  • Container: an isolated process created from an image. Containers are disposable unless you persist data.
  • Port mapping: connects a port on your computer to a port inside the container.
  • Volume: stores data outside a container’s writable layer so it can survive replacement.
  • Network: lets containers communicate. Compose normally creates a project network automatically.

Docker runs the application process in an isolated filesystem, network stack, and process tree. Compose adds a declarative way to define and operate several services as one application stack.

Check that Docker is installed

Run these commands in a terminal (PowerShell, Command Prompt, macOS Terminal, or a Linux shell):

docker --version
docker compose version

The first command reports the Docker CLI version. The second checks the modern Compose V2 plugin, which uses docker compose with a space. Older tutorials may show docker-compose with a hyphen; use the command provided by your current Docker installation.

Expected result: each command prints a version. If Docker Desktop is installed on Windows or macOS, make sure it is running. On Linux, the Docker Engine service must be running and your user may need permission to access the Docker socket.

Run your first container with docker run

Try a disposable web server:

docker run --name hello-web -d -p 8080:80 nginx
  • --name hello-web gives the container a predictable name.
  • -d runs it in the background (detached mode).
  • -p 8080:80 maps host port 8080 to port 80 in the container.
  • nginx is the image Docker will pull if it is not already local.

Open http://localhost:8080 in a browser. You should see the NGINX welcome page.

Tip: the left side of a port mapping is the port you use on your computer; the right side is the port the application listens to inside the container.

Inspect, stop, and remove the container

docker ps
docker logs hello-web
docker stop hello-web
docker rm hello-web

docker ps lists running containers. Add -a to include stopped containers. docker logs shows the process output, which is usually the first troubleshooting step. Stopping is safe; removing a container deletes that container object, but it does not delete the image.

Warning: docker rm removes the container. Do not use docker rm -f or volume-removal options on a container that holds data you still need. Back up persistent data before cleanup.

Images: pull, list, and update

docker image ls
docker pull nginx:latest
docker image inspect nginx:latest

docker image ls shows local images. docker pull downloads an image tag from a registry. docker image inspect displays metadata such as the entrypoint, exposed ports, and architecture. For repeatable projects, prefer a specific version tag instead of relying on latest; test upgrades before changing production workloads.

Why use Docker Compose?

Use Compose when an application needs more than one service—for example, a web app plus a database. A Compose file defines services, images or build instructions, ports, environment variables, volumes, and networks in one place. The current recommended format is the Compose Specification; you normally do not need a top-level version: field.

Create a simple Compose YAML file

Create a new folder and save the following as compose.yaml (or docker-compose.yml) inside it:

services:
  web:
    image: nginx:1.27-alpine
    ports:
      - "8080:80"
    depends_on:
      - redis

  redis:
    image: redis:7-alpine

This example defines two services:

  • web runs NGINX and publishes it at http://localhost:8080.
  • redis runs a Redis service on the private Compose network.
  • depends_on expresses startup order. It does not, by itself, prove that Redis is ready to accept connections; applications should still retry connections or use a health check when readiness matters.

Compose gives services a shared default network. From another container, the hostname redis resolves to the Redis service; do not use localhost to reach a different container.

Start and manage the Compose project

Run these commands from the folder containing compose.yaml:

docker compose config
docker compose up -d
docker compose ps
docker compose logs -f
docker compose down

docker compose config parses and renders the configuration, so it is a useful validation step before starting anything. up -d creates and starts the services in the background. ps reports their state and published ports. logs -f follows live logs; press Ctrl+C to stop following logs without stopping the services. down stops and removes the project’s containers and network.

Data note: this example has no database volume because Redis is only demonstrating service-to-service wiring. For real application data, define a named volume and understand the backup and deletion behavior before using docker compose down -v.

Common Compose YAML mistakes

  • Indentation: YAML uses spaces, not tabs. Keep child keys consistently indented.
  • Port collisions: change the host-side port (for example, 8081:80) if port 8080 is already in use.
  • Wrong hostname: use the Compose service name, such as redis:6379, for container-to-container connections.
  • Missing quotes: quote port mappings such as "8080:80" to keep YAML from interpreting them unexpectedly.
  • Assuming startup means readiness: add health checks and application retries for services that need to wait for a database or queue.

What to learn next

Once this workflow makes sense, learn how to write a Dockerfile, build your own image with docker build, inject configuration with an .env file, persist data with named volumes, and publish only the ports that must be reachable from the host. For production, also study image updates, secrets, resource limits, backups, and least-privilege container settings.

Official references

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top