What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Learn Docker in stages: install the right Docker environment for your operating system, build and run an image, use Compose to start related services, and address readiness, persistent data, and deployment settings before treating a local stack as production-ready. The examples below create a small web app backed by Redis.

What you’ll build and what each Docker file does

The lab uses a Flask web app that increments a counter stored in Redis. It separates three responsibilities:

  • Dockerfile: instructions for building the app’s image.
  • Compose file: services and their runtime configuration, including networking, environment variables, ports, and storage.
  • Container: a running instance of an image. You can replace a container without rebuilding the image.

Docker’s Compose documentation puts the distinction this way: “A Dockerfile provides instructions to build a container image while a Compose file defines your running containers.” Compose lets you declare related services together in YAML.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Install Docker and verify that it runs before starting the lab. The examples use the current Docker CLI and Compose plugin syntax: docker compose, with a space. They do not depend on the legacy standalone docker-compose command.

How to install Docker on your operating system

Choose the installation path for your platform rather than following a generic package-install command. Docker’s supported platforms and requirements can change; consult its current, distribution-specific installation instructions before installing.

Platform or need Recommended route What to know
Windows, macOS, or Linux desktop Docker Desktop Docker identifies Desktop as the easiest way to get Compose; it bundles Docker Engine, the CLI, and Compose. Check current platform requirements and terms.
Linux server or direct Linux Engine setup Docker Engine and the Compose plugin Use the instructions for your Linux distribution and architecture. Docker says derivatives of supported distributions may work, but are not tested or verified.
Existing Linux Engine and CLI installation Install the Compose plugin The standalone Compose option is marked legacy and supported for backward compatibility.

Docker’s Engine installation guidance distinguishes stable and test release channels; test releases can contain pre-release features that may break. For ordinary use, follow the current stable instructions unless you specifically need to evaluate a pre-release feature.

Docker’s installation page states that commercial use of Docker Engine obtained through Docker Desktop in larger enterprises exceeding 250 employees or $10 million USD in annual revenue requires a paid subscription. The scope and licensing terms can change, so check Docker’s current terms for your organization. The same page identifies Docker Engine as Apache License 2.0.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Verify the installation

Open a terminal and run Docker’s basic verification example:

docker run hello-world

Docker downloads and runs the small verification image if it is not already available locally, then prints a success message. If the command is unavailable, check that the installation completed and that Docker Desktop or the Engine is running; on Linux, follow the distribution-specific instructions for using the Engine with your account.

How to build and run your first application image

Create a new folder named docker-foundations. Inside it, create app.py, requirements.txt, and Dockerfile. This app exposes a page that counts visits in Redis; the next section will run both services together.

1. Create the application

Put this in app.py:

import os
import time

from flask import Flask
from redis import Redis

app = Flask(__name__)
redis = Redis(
    host=os.environ.get("REDIS_HOST", "redis"),
    port=6379,
    decode_responses=True,
)

@app.get("/")
def index():
    visits = redis.incr("visits")
    return f"Hello from Docker! This page has been visited {visits} times.n"

@app.get("/health")
def health():
    redis.ping()
    return "okn"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

Put these dependency names in requirements.txt:

Flask
redis

This minimal file lets pip select compatible releases when the image is built; it is not a dependency lock. For a repeatable application build, use a reviewed lock file with pinned dependencies.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

2. Write the Dockerfile

Put this in Dockerfile:

FROM python:3.12-slim

WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .

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

FROM selects a base image; WORKDIR sets the working directory; COPY adds files; RUN executes a build-time command; and CMD supplies the default process when a container starts. EXPOSE documents the app’s listening port; by itself, it does not publish that port on your host.

The python:3.12-slim tag is an example, not a claim that it is the right base image for every application. Choose a trusted, appropriately small base image, and decide how you will manage mutable tags: following a tag can make updates easier to receive, while pinning an image version or digest improves reproducibility but requires deliberate updates.

3. Exclude irrelevant files, then build and run

Create .dockerignore in the same folder:

.git
.venv
__pycache__
*.pyc
.env

The build context is the set of files Docker can access during a build. Excluding version-control metadata, local environments, caches, and environment files reduces accidental inclusion and avoids sending irrelevant files to the builder. Do not put credentials in an image.

Build the image, then run it with a host port mapping:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
docker build -t docker-foundations-web .
docker run --rm -p 127.0.0.1:8000:8000 docker-foundations-web

Open http://127.0.0.1:8000 in a browser. The app will return an error because this image does not include a Redis service yet; that is expected. Stop the process with Ctrl+C. You have built an image, but the app needs a second service to do useful work.

How to run the web app and Redis with Docker Compose

Compose gives each service a network name. The app can reach Redis at host name redis on the Compose network without you publishing Redis’s port to the host. Add this compose.yaml beside the Dockerfile:

services:
  web:
    build: .
    environment:
      REDIS_HOST: redis
    ports:
      - "127.0.0.1:8000:8000"
    depends_on:
      redis:
        condition: service_healthy

  redis:
    image: redis:7
    volumes:
      - redis-data:/data
    command: ["redis-server", "--appendonly", "yes"]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 10

volumes:
  redis-data:

Start the stack from the project directory:

docker compose up --build

Compose builds the app image and starts both services. When Redis reports healthy, Compose starts the web service. Visit http://127.0.0.1:8000 several times; the counter should increase. In another terminal, inspect the declared services with docker compose ps. Stop the foreground stack with Ctrl+C, or run docker compose down from the project directory.

Why startup ordering and readiness are different

A service being started does not necessarily mean it is ready to accept requests. Docker’s Compose tutorial calls out a race in which an application can start before Redis is ready. The healthcheck and depends_on condition above make Compose wait for Redis’s health check before starting the app. Applications should still handle transient connection failures; health checks are not a substitute for resilience if a dependency becomes unavailable later.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why Redis data needs a volume

Container filesystems include a writable layer, but that layer is tied to the container. Removing and recreating a container discards data stored only there. In this example, the named volume redis-data stores Redis data outside the container’s writable layer, and Redis append-only persistence is enabled for the lab.

Run docker compose down, then start the stack again with docker compose up --build. The visit count should remain because the named volume remains. To remove the stack and its stored volume deliberately, use docker compose down --volumes; that deletes the lab’s persisted counter. A named volume illustrates persistence, but production databases also need an appropriate backup and recovery plan.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How to make the Compose setup production-oriented

A successful local demo is not a production deployment. Docker recommends using an additional Compose file for production-specific configuration. Save the following as compose.production.yaml:

services:
  web:
    ports:
      - "8000:8000"
    restart: unless-stopped
    environment:
      FLASK_DEBUG: "0"
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

  redis:
    restart: unless-stopped
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

Use the production overlay explicitly:

docker compose -f compose.yaml -f compose.production.yaml up --build -d

The overlay publishes the web port without binding it only to the loopback interface, adds restart behavior, sets a non-debug environment value, and configures log rotation. Make the production port choice deliberately: a published port can be reachable beyond the local machine depending on its network and firewall configuration. Logging-driver behavior and available options depend on the Docker environment.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The lab’s FLASK_DEBUG value is not a substitute for a complete production configuration. Do not put secrets directly in a Compose file committed to source control; use an appropriate secrets-management method for your deployment. The sample Redis service and counter are for learning, not a complete production data architecture.

Best Value
Docker Container Linux Devops Programming Coding T-Shirt
  • Docker, Docker Swarm, Docker Compose, Programmer, Developer, Coding, Programming, Software Engineer, Code, DevOps, Deploy, Deployment, Kubernetes, Salt, Puppet, Chef, Terraform, Container, AWS, Azure, Cloud, Geek, Funny, Computer, Software, Tech, IT
  • Integration, Scrum, Compile, Compilation, Science, Bug, Debug, Python, Linux, Java, Javascript, Scala, Dotnet, Kotlin
  • Lightweight, Classic fit, Double-needle sleeve and bottom hem

Development settings should be separate

During development, a source-code bind mount can make edits visible without rebuilding the image. Production usually removes that mount so the app runs from the built image. Docker’s production guidance also identifies ports, environment variables, restart behavior, and logging as settings that may differ from development.

For this lab, a development-only override can be saved as compose.override.yaml and used with the base file:

services:
  web:
    volumes:
      - ./app.py:/app/app.py

Compose automatically reads the conventional compose.override.yaml when you run docker compose up with the base file. To avoid loading that development mount for the production command, pass both files explicitly with -f compose.yaml -f compose.production.yaml, as shown above.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

If you change app code that is copied into the image, rebuild and recreate the affected service. For example:

docker compose -f compose.yaml -f compose.production.yaml up --build -d web

Docker documents two useful build options with different effects: --pull asks the builder to check for a newer base image, while --no-cache reruns build steps without using the build cache. Use them intentionally, for example docker build --pull --no-cache -t docker-foundations-web . when you want both behaviors; neither option replaces a review of dependency and base-image updates.

What to learn next

Once this stack makes sense, continue with image layers and build-cache behavior, multi-stage and multi-architecture builds, volumes and networking, and orchestration concepts. Docker’s beginner learning path, self-guided training materials, and Docker 101 tutorial cover these subjects, including image building, containers, volumes, source mounts, and Compose. These official resources identify Docker Desktop, Git, and a code editor among the materials’ requirements.

The main learning path is available through Docker’s free official materials; a paid book is not required to complete these labs. An optional further-reading choice listed in Docker’s educational resources is Docker Deep Dive by Nigel Poulton, 2024 Edition. O’Reilly’s catalog lists a fifth edition from May 2025; check the publisher’s current listing for edition details.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.