What is Docker and Why Should You Care?
If you have ever said “but it works on my machine” during a project deployment, Docker exists to solve exactly that problem. Docker is a platform that packages your application and all its dependencies into a standardized unit called a container. This container runs identically on any machine, whether it is your laptop, your teammate’s computer, a staging server, or a production cloud environment. No more wrestling with different operating system versions, conflicting library installations, or mysterious configuration differences between development and production. Docker gives you consistency, portability, and reproducibility.
Understanding Containers vs. Virtual Machines
Before Docker, the standard approach to environment isolation was virtual machines (VMs). A VM runs a complete operating system on top of your host OS, which consumes significant memory and disk space. A single VM can easily use two or three gigabytes of RAM just for the operating system overhead. Containers, on the other hand, share the host operating system’s kernel and only package the application layer and its dependencies. This makes containers dramatically lighter. A typical Docker container uses 50 to 200 megabytes of memory and starts in seconds, compared to the minutes it takes to boot a VM. You can run dozens of containers on a single laptop without breaking a sweat.
Installing Docker
Docker Desktop is available for Windows, macOS, and Linux. Download it from the official Docker website and follow the installation wizard. On Windows, Docker Desktop uses WSL 2 (Windows Subsystem for Linux) as its backend, so make sure WSL 2 is enabled. On macOS, it runs inside a lightweight Linux VM managed transparently by Docker Desktop. After installation, open your terminal and run the following command to verify the installation:
docker --versionYou should see the Docker version number displayed. Then run the hello-world container to confirm everything is working:
docker run hello-worldIf you see a “Hello from Docker!” message, your installation is complete and functioning correctly.
Key Docker Concepts
- Image: A read-only template that contains the application code, runtime, libraries, and configuration files. Think of it as a blueprint or a snapshot of your application at a specific point in time.
- Container: A running instance of an image. You can create, start, stop, and delete containers. Multiple containers can run from the same image simultaneously.
- Dockerfile: A text file containing step-by-step instructions for building a Docker image. It specifies the base operating system, installs dependencies, copies your application code, and defines how the application should start.
- Docker Hub: A public registry where you can find and share Docker images. It hosts thousands of pre-built images for popular technologies like Node.js, Python, PostgreSQL, Redis, and Nginx.
Writing Your First Dockerfile
Let us containerize a simple Node.js web application. Create a new directory for your project and add a basic Express server in a file called app.js:
const express = require('express');
const app = express();
const PORT = 3000;
app.get('/', (req, res) => {
res.send('Hello from Docker!');
});
app.listen(PORT, () => {
console.log(`Server running on port `);
});Create a package.json file with Express as a dependency. Then create a file named Dockerfile (no extension) in the same directory:
# Use the official Node.js 20 image as the base
FROM node:20-alpine
# Set the working directory inside the container
WORKDIR /app
# Copy package files first (for better caching)
COPY package*.json ./
# Install dependencies
RUN npm install --production
# Copy the rest of the application code
COPY . .
# Expose port 3000
EXPOSE 3000
# Define the command to start the application
CMD ["node", "app.js"]Building and Running Your Container
Open your terminal in the project directory and build the Docker image:
docker build -t my-node-app .The -t flag tags the image with a name. The dot at the end tells Docker to use the current directory as the build context. Docker will execute each instruction in the Dockerfile sequentially, creating layers for each step. Now run the container:
docker run -p 3000:3000 my-node-appThe -p flag maps port 3000 on your host machine to port 3000 inside the container. Open your browser and navigate to http://localhost:3000. You should see “Hello from Docker!” displayed. Congratulations, your application is now running inside a container.
Using Docker Compose for Multi-Container Applications
Real-world applications rarely consist of a single container. You typically need a web server, a database, and possibly a caching layer. Docker Compose lets you define and manage multi-container applications using a single YAML file. Create a file named docker-compose.yml:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
environment:
- DATABASE_URL=postgres://user:pass@db:5432/mydb
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=mydb
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:Start the entire stack with a single command:
docker compose upDocker Compose creates a private network for your services, so the web container can reach the database container simply by using “db” as the hostname. This setup mirrors production environments and makes development incredibly smooth.
Best Practices for Production
- Use multi-stage builds: Keep your final image small by using a build stage to compile assets and a separate runtime stage that only includes what is needed to run the application.
- Never store secrets in images: Use environment variables or Docker secrets for API keys, database passwords, and other sensitive information.
- Use .dockerignore: Create a .dockerignore file to exclude node_modules, .git, and other unnecessary files from the build context. This speeds up builds and reduces image size.
- Pin your base image versions: Always use specific version tags like node:20-alpine instead of node:latest. This ensures your builds are reproducible and prevents unexpected breaking changes.
Conclusion
Docker eliminates the “it works on my machine” problem by packaging your application and its dependencies into portable, consistent containers. Whether you are a solo developer working on side projects or part of a team deploying to cloud infrastructure, Docker simplifies your workflow and ensures your application behaves the same everywhere it runs. Start with a simple Dockerfile, experiment with Docker Compose for multi-service applications, and follow production best practices from the beginning. Once you experience the reliability and convenience of containerized development, you will never want to go back.
