Skip to content
First 20 students get 50% discount.
Login/Register
Call: 123 4561 5523
Email: info@edublink.co
legendarywaysacademy.comlegendarywaysacademy.com
  • Category
    • Business
    • Cooking
    • Digital Marketing
    • Fitness
    • Motivation
    • Online Art
    • Photography
    • Programming
    • Yoga
  • Home
      • EduBlink EducationHOT
      • Distant Learning
      • University
      • Online AcademyHOT
      • Modern Schooling
      • Kitchen Coach
      • Yoga Instructor
      • Kindergarten
      • Language Academy
      • Remote Training
      • Business Coach
      • Motivation
      • Programming
      • Online Art
      • Sales CoachNEW
      • Quran LearningNEW
      • Gym TrainingNEW
      • PhotographyNEW
      • Health CoachNEWHOT
      • Digital MarketingNEWHOT
  • Pages
    • About Us
      • About Us 1
      • About Us 2
      • About Us 3
    • Instructors
      • Instructor 1
      • Instructor 2
      • Instructor 3
      • Instructor Details
    • Event Pages
      • Event Style 1
      • Event Details
    • Shop Pages
      • Product Details
    • Zoom Meeting
    • FAQ’s
    • Instructor Registration
    • Student Registration
    • Pricing Table
    • Privacy Policy
    • Coming Soon
    • 404 Page
  • Courses
    • Courses Style
      • Course Style 1
      • Course Style 2
      • Course Style 3
      • Course Style 4
      • Course Style 5
      • Course Style 6
      • Course Style 7
      • Course Style 8
      • Course Style 9
      • Course Style 10
      • Course Style 11
      • Course Style 12
      • Course Style 13
    • Course Details
      • Course Details 1
      • Course Details 2
      • Course Details 3
      • Course Details 4
      • Course Details 5
    • Course Filter
      • Filter Sidebar Left
      • Filter Sidebar Right
      • Filter Category
  • Blog
    • Blog Style 1
    • Blog Style 2
    • Blog Standard
    • Blog Details
  • Contact
    • Contact Us
    • Contact Me
0

Currently Empty: $0.00

Continue shopping

Try for free
legendarywaysacademy.comlegendarywaysacademy.com
  • Home
      • EduBlink EducationHOT
      • Distant Learning
      • University
      • Online AcademyHOT
      • Modern Schooling
      • Kitchen Coach
      • Yoga Instructor
      • Kindergarten
      • Language Academy
      • Remote Training
      • Business Coach
      • Motivation
      • Programming
      • Online Art
      • Sales CoachNEW
      • Quran LearningNEW
      • Gym TrainingNEW
      • PhotographyNEW
      • Health CoachNEWHOT
      • Digital MarketingNEWHOT
  • Pages
    • About Us
      • About Us 1
      • About Us 2
      • About Us 3
    • Instructors
      • Instructor 1
      • Instructor 2
      • Instructor 3
      • Instructor Details
    • Event Pages
      • Event Style 1
      • Event Details
    • Shop Pages
      • Product Details
    • Zoom Meeting
    • FAQ’s
    • Instructor Registration
    • Student Registration
    • Pricing Table
    • Privacy Policy
    • Coming Soon
    • 404 Page
  • Courses
    • Courses Style
      • Course Style 1
      • Course Style 2
      • Course Style 3
      • Course Style 4
      • Course Style 5
      • Course Style 6
      • Course Style 7
      • Course Style 8
      • Course Style 9
      • Course Style 10
      • Course Style 11
      • Course Style 12
      • Course Style 13
    • Course Details
      • Course Details 1
      • Course Details 2
      • Course Details 3
      • Course Details 4
      • Course Details 5
    • Course Filter
      • Filter Sidebar Left
      • Filter Sidebar Right
      • Filter Category
  • Blog
    • Blog Style 1
    • Blog Style 2
    • Blog Standard
    • Blog Details
  • Contact
    • Contact Us
    • Contact Me

Docker and Containers for DevOps

  • Home
  • DevOps Topics
  • Docker and Containers for DevOps
Breadcrumb Abstract Shape
Breadcrumb Abstract Shape
Breadcrumb Abstract Shape
Docker and containers illustration showing stacked container image layers
Legendary Ways Academy · Delivery

Docker and Containers, Package Once, Run Anywhere

“Works on my machine” stops being an excuse once your application ships as a container. This is the guide to building, running, and actually understanding Docker images.

Next: Kubernetes All Topics
Real Dockerfile
Multi-stage builds
Compose examples
Docker and containers illustration showing stacked container image layers

A container packages an application together with everything it needs to run, code, runtime, system libraries, configuration, into a single portable unit that behaves identically whether it’s running on your laptop, a teammate’s machine, a CI runner, or a production server. Docker is the tool that made containers mainstream, providing the format for building images and the runtime for running them as containers. Before containers, “works on my machine” was a genuine, frequent problem: subtle differences in installed library versions, OS configuration, or environment variables between a developer’s laptop and the production server caused bugs that were maddening to reproduce. Containers eliminate that entire class of problem by shipping the actual runtime environment along with the code.

This guide covers building your first image with a real Dockerfile, the layer caching model that makes builds fast, multi-stage builds for smaller production images, running multi-container applications with Docker Compose, and the security and debugging habits that come up constantly in real DevOps work. It also covers the parts that come up once you move past a single toy container: persisting data with volumes, networking between containers, layering environment-specific configuration, and pushing built images to a registry so they can actually be deployed somewhere other than your own laptop.

None of this requires deep prior infrastructure experience. If you’re comfortable with the basic Linux commands covered in our command line guide, you already have most of the mental model containers build on; Docker mainly adds a packaging and isolation layer around commands and processes you likely already understand conceptually.

Containers vs. Virtual Machines

Containers

  • Share the host machine’s OS kernel
  • Start in milliseconds to seconds
  • Lightweight, typically megabytes in size
  • Isolated processes, not full separate operating systems

Virtual Machines

  • Run a full separate guest operating system
  • Take minutes to boot
  • Heavier, typically gigabytes in size
  • Stronger isolation, at a real resource cost

Containers are cheaper and faster because they don’t virtualize an entire operating system, they isolate processes using kernel features while sharing the underlying OS with the host and other containers. That’s what makes it practical to run dozens of containers on a single server, something that would be impractical with dozens of full virtual machines.

Your First Dockerfile

A Dockerfile is a text file of instructions describing how to build an image, step by step: what base image to start from, what to copy in, what to install, and what command to run when the container starts.

dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
bash
docker build -t my-app:latest .
docker run -p 3000:3000 my-app:latest
docker ps
docker logs <container-id>

FROM sets the base image (here, a minimal Alpine Linux build of Node 20). WORKDIR sets the working directory inside the container. COPY brings files in from your local machine. RUN executes a command during the build (installing dependencies here). EXPOSE documents which port the app listens on, and CMD defines what runs when the container starts. docker build creates the image; docker run starts a container from it, mapping port 3000 on your machine to port 3000 inside the container.

Layer Caching: Why Instruction Order Matters

Docker builds images in layers, one per instruction, and caches each layer. If a layer’s inputs haven’t changed since the last build, Docker reuses the cached result instead of rebuilding it, which is why the Dockerfile above copies package*.json and installs dependencies before copying the rest of the application code. Dependencies change far less often than application code, so structuring the Dockerfile this way means most builds skip the slow npm ci step entirely and only rebuild the fast final layers where the actual code changed.

Getting this instruction order wrong, copying all the code before installing dependencies, means every single code change invalidates the dependency-install layer too, turning what should be a five-second rebuild into a multi-minute one on every iteration. This is one of the most common and highest-impact Dockerfile mistakes for teams new to containers.

Multi-Stage Builds for Smaller Images

A multi-stage build uses more than one FROM instruction, letting you compile or build in one stage with all the necessary build tools, then copy only the final compiled artifact into a much smaller final image that doesn’t carry the build tooling into production.

dockerfile
FROM node:20 AS build
WORKDIR /app
COPY . .
RUN npm ci && npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=build /app/dist ./dist
COPY package*.json ./
RUN npm ci --production
CMD ["node", "dist/server.js"]

The first stage has the full Node toolchain needed to build the application; the second stage starts fresh from a minimal Alpine image and copies over only the compiled output. The result is a production image that’s often a fraction of the size of a single-stage build, which matters for both faster deploys and a smaller attack surface.

Running Multi-Container Apps With Docker Compose

Real applications rarely run as a single container; there’s usually a database, a cache, and one or more services that need to talk to each other. Docker Compose defines a multi-container application in a single YAML file, letting you start the whole stack with one command.

yaml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/mydb
    depends_on:
      - db
  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_PASSWORD=pass
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Running docker compose up starts both the application and its database, connected on a shared network where app can reach the database simply by hostname (db), no manual network configuration required. The named volume (db-data) persists the database’s data across container restarts, so stopping and starting the stack doesn’t wipe your data each time.

Volumes: Persisting Data Beyond a Container’s Lifetime

Containers are ephemeral by design; when a container is removed, anything written to its filesystem disappears with it. Volumes solve this for data that needs to survive, database files, uploaded content, by storing it outside the container’s writable layer. Docker offers two main approaches: named volumes, managed by Docker itself and portable across environments, and bind mounts, which map a specific directory on the host machine directly into the container.

bash
# Named volume, managed by Docker
docker run -v db-data:/var/lib/postgresql/data postgres:16-alpine

# Bind mount, maps a specific host directory
docker run -v $(pwd)/src:/app/src my-app:latest

Named volumes are generally preferred for production data like databases, since Docker manages their location and lifecycle consistently across environments. Bind mounts are especially useful in local development, mapping your source code directory directly into a running container so code changes on your machine appear instantly inside the container without rebuilding the image each time.

Container Networking Basics

By default, Docker creates an isolated network for containers started via Compose, and containers on that network can reach each other by service name, exactly how the example above lets the app service connect to db without hardcoding an IP address. Ports only become reachable from outside Docker when explicitly published with -p or a ports entry in Compose; anything not published stays isolated from the host machine and the outside world, which is a sensible security default, not an oversight.

bash
docker network ls
docker network inspect bridge
docker run --network my-custom-net my-app:latest

Understanding this default isolation matters when debugging “why can’t my container reach X”: the answer is very often that a port was never published, or two containers that need to talk to each other were never placed on the same network in the first place.

Environment-Specific Configuration

A common real-world pattern is layering Compose files: a base docker-compose.yml defining the shared shape of the application, with an override file adding development-specific conveniences (like bind-mounted source code and debug ports) or production-specific settings (like resource limits and restart policies).

yaml
# docker-compose.override.yml (development, loaded automatically)
services:
  app:
    volumes:
      - ./src:/app/src
    environment:
      - NODE_ENV=development
    command: npm run dev

Docker Compose automatically merges docker-compose.override.yml on top of the base file when present, so running docker compose up locally picks up development conveniences automatically, while a production deploy can explicitly specify a different, production-focused override file instead.

Pushing Images to a Registry

A built image needs to live somewhere accessible before it can be deployed elsewhere, that’s what a container registry is for. Docker Hub is the default public registry, but most teams use a private registry, AWS ECR, Azure Container Registry, or GitHub Container Registry, particularly for proprietary application code.

bash
docker build -t myregistry.azurecr.io/my-app:v1.2.0 .
docker login myregistry.azurecr.io
docker push myregistry.azurecr.io/my-app:v1.2.0

Tagging images with a specific version (v1.2.0) rather than only ever using latest matters in production: latest is a moving target that changes meaning every time a new image is pushed, making it impossible to know exactly what’s actually running or to reliably roll back to a specific prior version. Most production deployment pipelines, covered in our CI/CD pipelines guide, build and push a uniquely tagged image on every release specifically to avoid this ambiguity.

Debugging a Running Container

When something inside a container isn’t behaving as expected, a handful of commands cover most debugging needs. docker logs shows a container’s output. docker exec runs a command inside an already-running container, most commonly to open an interactive shell and poke around directly.

bash
docker logs -f my-container
docker exec -it my-container sh
docker inspect my-container
docker stats

docker exec -it my-container sh drops you into an interactive shell inside the running container, from which you can run the same Linux commands covered in our Linux and command line guide to investigate what’s actually happening. docker stats shows live CPU and memory usage per container, useful when a container is behaving unexpectedly and you need to know if it’s a resource constraint.

Container Security Basics

A few habits meaningfully reduce a container’s attack surface. Avoid running as root inside the container when possible, using a USER instruction to switch to a non-privileged user. Use minimal base images (Alpine or distroless variants) rather than full OS images, which reduces both image size and the number of packages that could carry a known vulnerability. Scan images for known vulnerabilities as part of the CI pipeline, using a tool like Trivy or Docker Scout, and never bake secrets directly into an image layer, since layers persist in the image and remain extractable even if a later layer appears to remove the secret.

dockerfile
FROM node:20-alpine
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup . .
USER appuser
CMD ["node", "server.js"]

Creating a dedicated non-root user and switching to it with USER before the container’s main process runs means that even if an attacker manages to exploit a vulnerability in the running application, they land with the limited permissions of that unprivileged user rather than full root access inside the container, meaningfully limiting the blast radius of a successful exploit.

How This Connects to the Rest of DevOps

Docker images are frequently the artifact a CI/CD pipeline builds and the unit that Kubernetes orchestrates at scale. The Dockerfile instructions covered here run inside the same Linux environment covered in our command line guide, and the package-management commands from that guide are exactly what shows up inside a Dockerfile’s RUN instructions. Containers are the connective format that makes the rest of a modern DevOps toolchain, from local development through to production orchestration, consistent.

Frequently Asked Questions

Do I need Docker Desktop, or can I use the command line only?

Command line only works fine and is what most production and CI environments use; Docker Desktop adds a GUI on top that’s convenient for local development but not required to learn or use Docker effectively.

Why is my image so much larger than expected?

Usually unnecessary files copied into the image (check for a missing .dockerignore), a full OS base image instead of a slim variant, or a single-stage build carrying build tools into the final image unnecessarily.

What’s the difference between an image and a container?

An image is the built, static package (the blueprint); a container is a running instance of that image. You can run multiple containers from the same image simultaneously.

Should I learn Docker before Kubernetes?

Yes. Kubernetes orchestrates containers at scale, but assumes you already understand what a container and an image actually are; skipping straight to Kubernetes without this foundation makes everything harder to reason about.

My container exits immediately after starting. Why?

A container stops as soon as its main process (the one defined by CMD or ENTRYPOINT) exits. If that process is meant to run continuously (like a web server) but exits immediately, check for a startup error with docker logs, since the container isn’t crashing arbitrarily, it’s correctly reflecting that its main process ended.

What’s a .dockerignore file for?

It works like .gitignore but for the build context: excluding files (like node_modules, .git, or local environment files) from being copied into the image, keeping builds faster and images smaller, and preventing local secrets from accidentally ending up baked into an image layer.

If you’re building this skill toward a DevOps role, prioritize actually containerizing a real application end to end over reading about containers in the abstract. Take an existing project, write a real Dockerfile for it, get it running with Compose alongside a database, and push it to a registry. That hands-on sequence, especially debugging the inevitable issues that come up (a missing environment variable, a networking misconfiguration, an oversized image), builds far more real fluency than following a tutorial’s copy-paste example without hitting any friction along the way.

Related reading: continue to Kubernetes, revisit the Linux and command line guide for the fundamentals containers build on, or return to the full topics overview.

logo-dark

Lorem ipsum dolor amet consecto adi pisicing elit sed eiusm tempor incidid unt labore dolore.

Add: 70-80 Upper St Norwich NR2
Call: +01 123 5641 231
Email: info@edublink.co

Online Platform

  • About
  • Course
  • Instructor
  • Events
  • Instructor Details
  • Purchase Guide

Links

  • Contact Us
  • Gallery
  • News & Articles
  • FAQ’s
  • Coming Soon

Contacts

Enter your email address to register to our newsletter subscription

Icon-facebook Icon-linkedin2 Icon-instagram Icon-twitter Icon-youtube
Copyright 2026 EduBlink | Developed By DevsBlink. All Rights Reserved
legendarywaysacademy.comlegendarywaysacademy.com

Sign in

Lost your password?

Sign up

Already have an account? Sign in