How to Use Docker: Compose, Kubernetes & CI/CD for Beginners

2026-06-05·Advanced Guides

Key Takeaways

  • Docker containers run isolated apps with minimal overhead—use 10-50 MB images vs 1-10 GB VMs.
  • Docker Compose simplifies multi-container setups with a single YAML file. Save hours per project.
  • Kubernetes orchestrates containers at scale; start with Minikube for local testing.
  • CI/CD pipelines with Docker cut deployment time by 40-60% in my experience.

---

Getting Started with Docker Containers

Docker packages your app and its dependencies into a lightweight container. Unlike virtual machines (VMs), containers share the host OS kernel, so they start in seconds, not minutes. For example, a simple Node.js app runs in a ~150 MB image vs a 2 GB VM.

Step 1: Install Docker

  • Download from docker.com. On Ubuntu 22.04, I run `sudo apt install docker.io`.
  • Verify with `docker --version`. You want 24.0 or later.

Step 2: Your First Container

```bash

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

```

This pulls the Nginx image (22 MB compressed) and starts it on port 8080. Open http://localhost:8080—you’ll see the default page.

Step 3: Build Your Own Image

Create a `Dockerfile`:

```dockerfile

FROM node:18-alpine

WORKDIR /app

COPY package.json .

RUN npm install

COPY . .

CMD ["node", "server.js"]

```

Build with `docker build -t myapp .` (takes ~30 seconds on a good connection).

---

Docker Compose for Multi-Container Apps

Single containers are fine for testing, but real apps need databases, caches, and queues. Docker Compose ties them together in one file.

Example: Node App + PostgreSQL

Create `docker-compose.yml`:

```yaml

version: '3.8'

services:

app:

build: .

ports:

- "3000:3000"

environment:

DB_HOST: db

db:

image: postgres:15-alpine

environment:

POSTGRES_PASSWORD: secret

volumes:

- pgdata:/var/lib/postgresql/data

volumes:

pgdata:

```

Run `docker-compose up -d`. Both services start in under 5 seconds. I once spent 3 hours debugging a connection string—don’t forget `DB_HOST: db`, not `localhost`.

Why Compose?

  • Single command for everything.
  • Volume mounts preserve data across restarts.
  • Easy to share setups with teammates.

---

Kubernetes Basics (Without the Panic)

Kubernetes (K8s) manages container clusters. It’s overkill for one app, but essential for scaling. Start locally with Minikube.

Step 1: Install Minikube

```bash

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64

sudo install minikube-linux-amd64 /usr/local/bin/minikube

minikube start --driver=docker

```

This spins up a single-node cluster with 2 CPUs and 2 GB RAM.

Step 2: Deploy a Pod

A pod is the smallest K8s unit. Save as `pod.yaml`:

```yaml

apiVersion: v1

kind: Pod

metadata:

name: nginx

spec:

containers:

- name: nginx

image: nginx:latest

ports:

- containerPort: 80

```

Run `kubectl apply -f pod.yaml`. Check with `kubectl get pods`.

Comparison: Docker vs Kubernetes

FeatureDocker ComposeKubernetes
-------------------------------------
ScaleManualAuto-scaling
NetworkingSimpleComplex (Services, Ingress)
PersistenceVolumesPersistentVolumeClaims
Learning curveLowHigh

When to use K8s: You have >5 services or need zero-downtime deployments. For a side project, stick with Compose.

---

CI/CD Pipelines with Docker

Continuous Integration/Deployment (CI/CD) automates building and deploying containers. I use GitHub Actions for most projects.

Example: Deploy to Docker Hub

Create `.github/workflows/deploy.yml`:

```yaml

name: Docker CI/CD

on:

push:

branches: [main]

jobs:

build:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v3

- name: Log in to Docker Hub

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

- name: Build and push

run: |

docker build -t myuser/myapp:latest .

docker push myuser/myapp:latest

```

Every push to `main` triggers a build. My team cut deployment time from 20 minutes to 8 minutes using this pattern.

Pro tip: Tag images with commit hashes (`docker build -t myapp:${{ github.sha }}`) for easy rollbacks.

Testing in CI

Before deploying, run tests inside the container:

```bash

docker run --rm myapp:latest npm test

```

If tests fail, the pipeline stops—no bad code shipped.

---

Common Mistakes to Avoid

1. Running containers as root – Use `USER node` in Dockerfile for security.

2. Ignoring .dockerignore – Add `node_modules` and `.git` to keep builds small. A 300 MB image takes 3x longer to push.

3. Forgetting to remove unused images – `docker system prune -a` frees gigabytes. I reclaim 5-10 GB monthly.

---

FAQ

Q: Do I need Kubernetes if I only have one service?

A: No. Docker Compose handles that fine. Kubernetes adds complexity without benefit for single-service apps. Use it when you need scaling or multiple microservices.

Q: How do I persist data in Docker containers?

A: Use volumes. In Compose, define a volume under `volumes:` and mount it with `- /path/in/container`. Data survives container restarts.

Q: Why is my container not connecting to the database?

A: Check networking. In Compose, services communicate by service name (e.g., `db`). In standalone Docker, use `--network` flag or `--link`. Also verify environment variables.

---

*Start small. Run one container. Add Compose. Explore Kubernetes when you hit limits. Docker isn’t magic—it’s just better packaging.*