Docker for Developers: Images, Containers, and Compose

DevOps2026-10-06TryQuickToolBox

You've built a web app that runs perfectly on your machine, but when you deploy it to a server or share it with a teammate, it breaks. Dependencies are missing, versions conflict, or environment variables are wrong. This is the classic "it works on my machine" problem. Docker solves this by packaging your application and its dependencies into a standardized unit called a container. In this guide, you'll learn the core concepts of Docker—images, containers, and Compose—and how to use them to streamline your development workflow.

What is Docker?

Docker is a platform for building, running, and shipping applications in containers. A container is a lightweight, standalone, executable package that includes everything needed to run a piece of software: code, runtime, system tools, libraries, and settings. Containers isolate applications from each other and from the underlying infrastructure, ensuring consistency across environments.

Images vs. Containers

An image is a read-only template with instructions for creating a container. It's like a snapshot of your application and its environment. A container is a runnable instance of an image. You can create, start, stop, move, or delete containers using the Docker API or CLI.

Think of an image as a class and a container as an object (instance) of that class. You can run multiple containers from the same image, each isolated from the others.

Building an Image with a Dockerfile

A Dockerfile is a text file with instructions to build an image. Here's a simple example for a Node.js app:

# Use an official Node.js runtime as a parent image
FROM node:18-alpine

# Set the working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy the rest of the application code
COPY . .

# Expose the port the app runs on
EXPOSE 3000

# Define the command to run the app
CMD ["node", "server.js"]

To build the image, run:

docker build -t my-node-app .

The -t flag tags the image with a name (my-node-app). The . at the end specifies the build context (current directory).

Running Containers

Once you have an image, you can run a container:

docker run -p 3000:3000 -d my-node-app

This maps port 3000 on your host to port 3000 in the container and runs it in detached mode (-d).

Common commands:

Managing Data with Volumes

Containers are ephemeral; when they are removed, their data is lost. To persist data, use volumes. A volume is a directory outside the container's filesystem, managed by Docker.

docker run -v my_volume:/app/data my-node-app

You can also mount a host directory (bind mount) for development:

docker run -v $(pwd):/app my-node-app

Docker Compose: Multi-Container Applications

Real-world apps often require multiple services: a web server, a database, a cache, etc. Docker Compose lets you define and run multi-container Docker applications using a YAML file. Here's an example docker-compose.yml for a Node.js app with a PostgreSQL database:

version: '3.8'
services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:password@db:5432/mydb
    depends_on:
      - db
  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
volumes:
  postgres_data:

Run the entire stack with:

docker compose up -d

This builds the web service, pulls the Postgres image, creates a network, and starts both containers. The depends_on ensures the database starts before the web service.

To stop and remove containers, networks, and volumes:

docker compose down -v

Best Practices for Docker in Development

Comparison: Docker vs. Virtual Machines

Aspect Docker Containers Virtual Machines
Startup time Seconds Minutes
Resource usage Lightweight (shares OS kernel) Heavy (full OS per VM)
Isolation Process-level Hardware-level
Portability High (runs anywhere Docker runs) Limited by hypervisor compatibility

FAQ

What is the difference between an image and a container?

An image is a read-only template that defines the application and its environment. A container is a running instance of an image. You can create multiple containers from the same image.

When should I use Docker Compose?

Use Docker Compose when your application consists of multiple services (e.g., web server, database, cache) that need to work together. It simplifies orchestration for development and testing.

How do I persist data in Docker?

Use volumes to persist data outside the container's filesystem. Volumes are managed by Docker and can be shared between containers. Bind mounts can also be used to mount host directories.

Ready to containerize your next project? Start by writing a Dockerfile for a simple app, then gradually add services with Docker Compose. For more developer tools, check out our JSON Formatter to validate and beautify your configuration files.