How to Use Docker: Containerization with Compose, Kubernetes & CI/CD

2026-06-05·Tips & Tricks

Key Takeaways

  • Docker containers are lightweight virtual environments that share the host OS kernel, reducing overhead compared to VMs.
  • Docker Compose simplifies multi-container setups with a single YAML file.
  • Kubernetes orchestrates containers across clusters, handling scaling and recovery automatically.
  • CI/CD pipelines with Docker can cut deployment time from hours to minutes when done right.

---

Getting Started with Docker: What You Actually Need

Docker lets you package software into standardized units called containers. Unlike virtual machines that each carry their own OS, containers share the host OS kernel. This means a container might run in 50 milliseconds while a VM takes 30 seconds to boot. I’ve seen teams run 10 containers on a single laptop with 8GB RAM that would choke on 3 VMs.

First, install Docker Desktop from [docker.com](https://www.docker.com/products/docker-desktop/). It’s free for personal use and includes the CLI, Docker Compose, and a local Kubernetes cluster. On Linux, you can just install the engine with `sudo apt install docker.io` on Ubuntu.

Your First Container: The Hello World of Docker

Open a terminal and run:

```bash

docker run hello-world

```

This downloads a tiny image (about 13KB compressed) and runs it. You’ll see a message explaining Docker’s workflow. If you get a permission error on Linux, add your user to the docker group: `sudo usermod -aG docker $USER` then log out and back in.

Now let’s run an actual web server:

```bash

docker run -d -p 8080:80 nginx:alpine

```

  • `-d` runs in background
  • `-p 8080:80` maps your host’s port 8080 to the container’s port 80
  • `nginx:alpine` is a lightweight Nginx image (about 22MB vs 180MB for full Nginx)

Open http://localhost:8080 and you’ll see the Nginx welcome page.

Docker Compose: Managing Multi-Container Apps

When your app needs a database, cache, and backend, running containers individually gets messy. Docker Compose lets you define everything in a single YAML file.

Create a `docker-compose.yml` file:

```yaml

version: '3.8'

services:

web:

image: nginx:alpine

ports:

- "8080:80"

db:

image: postgres:13-alpine

environment:

POSTGRES_PASSWORD: example

volumes:

- pgdata:/var/lib/postgresql/data

volumes:

pgdata:

```

Run `docker-compose up -d`. This pulls both images, starts them, and creates a named volume for PostgreSQL data. To stop everything: `docker-compose down`. I use this pattern daily for local development — it’s saved me hours of manual setup.

Kubernetes Basics: When One Server Isn’t Enough

Kubernetes (K8s) manages containers across multiple machines. Docker Desktop includes a single-node Kubernetes cluster you can enable in settings (under Kubernetes tab).

First, create a deployment. Save this as `deployment.yaml`:

```yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: nginx-deployment

spec:

replicas: 3

selector:

matchLabels:

app: nginx

template:

metadata:

labels:

app: nginx

spec:

containers:

- name: nginx

image: nginx:alpine

ports:

- containerPort: 80

```

Apply it: `kubectl apply -f deployment.yaml`. Then expose it as a service:

```bash

kubectl expose deployment nginx-deployment --type=NodePort --port=80

```

Now run `kubectl get services` – you’ll see a port like 31234. Access it at http://localhost:31234. Kubernetes keeps 3 containers running – if one dies, it spins up a replacement automatically.

Comparison: Docker Compose vs Kubernetes

FeatureDocker ComposeKubernetes

-------------------------------------
Setup complexityLow – single fileHigher – many YAML resources
ScalingManual via `--scale`Automatic via replicas and HPA
NetworkingSimple DNS per serviceComplex with Ingress, Services
Use caseLocal dev, small deploymentsProduction, multi-node clusters
Learning curve1-2 hours2-4 weeks for basics

CI/CD Pipelines with Docker

Continuous integration and deployment (CI/CD) with Docker means building an image, testing it, then pushing to a registry. GitHub Actions makes this straightforward.

Here’s a minimal workflow (`.github/workflows/docker.yml`):

```yaml

name: Build and Push Docker Image

on:

push:

branches: [main]

jobs:

build:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v3

- name: Build image

run: docker build -t myapp:${{ github.sha }} .

- name: Push to Docker Hub

run: |

echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin

docker push myapp:${{ github.sha }}

```

Set your Docker Hub credentials as repository secrets (Settings > Secrets). Every push to main builds and pushes an image tagged with the commit hash. I’ve used this pattern in production – it cuts manual deployment errors to near zero.

Common Pitfalls I’ve Seen Beginners Make

1. Ignoring .dockerignore – You don’t want node_modules or .git in your image. Add a file with those patterns.

2. Running containers as root – Use `USER` in your Dockerfile to switch to non-root. Security matters.

3. Not cleaning up – `docker system prune -a` once a month reclaims gigabytes.

4. Using latest tag – Pin to specific versions like `nginx:1.25-alpine`. `latest` breaks when updates happen.

FAQ

Q: What’s the difference between a Docker image and a container?

A: An image is a read-only template (like a class in programming). A container is a running instance of that image (like an object). You can have many containers from the same image.

Q: Do I need Kubernetes for a small project?

A: Probably not. Docker Compose handles most single-server needs. Kubernetes adds complexity that pays off only when you have multiple servers or need auto-scaling. I’d recommend it for teams of 5+ or production services handling more than 10K requests per second.

Q: How do I persist data in Docker containers?

A: Use volumes. In Docker run: `-v myvolume:/data`. In Compose: add a `volumes:` section (like the PostgreSQL example above). Volumes survive container restarts and removals. Avoid bind mounts for production – they’re useful for development but cause permission issues.