Docker Use Cases #
Docker isn’t a tool created for one specific purpose. It’s a generic platform that can be applied in many contexts — from a developer’s laptop, to CI/CD pipelines, to Kubernetes clusters in the cloud. Because of that flexibility, the question isn’t “can Docker be used here?” but “how much value does Docker bring to this context, and what are the trade-offs?”
This article covers the most common and relevant Docker use cases in industry. For each use case, you’ll see the problem it solves, a concrete example, the main benefits, and important caveats to watch out for. At the end there’s a summary table you can use as a cheat sheet when making decisions.
The goal isn’t to make Docker seem usable for everything. There are situations where Docker genuinely isn’t the best choice — and we’ll discuss those honestly at the end.
Local Development Environment #
The most universal use case, and the one whose value you feel fastest. Almost every developer learning Docker starts here.
The Problem It Solves #
Ever heard “it works on my laptop, but errors on yours”? This happens because every laptop has its own OS, library, and configuration conditions. Developer A uses macOS with Python 3.11, developer B uses Windows with Python 3.9, developer C uses Linux with Python 3.12. Result: the same code behaves differently.
Docker answers this with one principle: all developers run the exact same stack, defined in a single file.
Concrete Example #
A team has an application that needs:
- A backend API (Go 1.22 + Postgres 16 + Redis 7).
- A worker (Python 3.12 + Kafka).
- A frontend (Node 20 + Nginx).
Without Docker, every developer has to install Go, Postgres, Redis, Python, Kafka, Node, Nginx — each with versions that must match. With Docker, a single docker-compose.yml file defines everything, and one command runs it all:
# docker-compose.yml
services:
api:
build: ./api
ports:
- "8080:8080"
depends_on:
- postgres
- redis
worker:
build: ./worker
depends_on:
- kafka
web:
build: ./web
ports:
- "3000:3000"
postgres:
image: postgres:16
environment:
POSTGRES_PASSWORD: dev
redis:
image: redis:7
kafka:
image: bitnami/kafka:3.7
A new developer just does git clone, then docker compose up. Within 5 minutes, the entire stack is running on their laptop — identical to what runs in staging and production.
Benefits #
- Fast onboarding for new developers. No need to set up environments one by one.
- Consistent environment for the whole team. No more “works on my machine”.
- Keeps the host clean. No Postgres or Redis installed globally on the laptop.
- Multiple projects coexist. You can run different stacks at the same time without conflicts.
Practical tips: Keepdocker-compose.ymlat the repo root, and adddocker compose upto the README. New developers can be productive in minutes, not days. For larger projects, separatedocker-compose.override.yml(local dev configuration) fromdocker-compose.yml(service definitions) so both can be version-controlled without conflicts.
Application Isolation #
The second use case you’ll feel most directly: running several applications that need conflicting dependencies on a single host.
The Problem It Solves #
On a traditional server, all applications share the same runtime and libraries. Application A needs an old library version, application B needs a new one — and both have to coexist on one OS.
Concrete Example #
Imagine one server with three applications:
- App A — an old microservice, needs Python 3.8 + C library version 1.0.
- App B — a new service, needs Python 3.12 + C library version 2.0.
- App C — an analytics script, needs Python 2.7 (legacy, can’t be upgraded).
Without Docker, the server admin has to pick one Python and C library version, then fiddle with symlinks or venvs — a fragile solution that often breaks after OS updates.
With Docker, each application runs in a container with its own image:
# App A
FROM python:3.8-slim
RUN apt-get install -y libfoo-dev=1.0
COPY . /app
CMD ["python", "/app/main.py"]
# App B
FROM python:3.12-slim
RUN apt-get install -y libfoo-dev=2.0
COPY . /app
CMD ["python", "/app/main.py"]
All three run on the same server, mutually isolated, without conflicts.
Benefits #
- Dependency isolation. Each application carries its own dependencies.
- Clean removal. Delete the container, and nothing is left behind.
- Safe updates. Updating one application doesn’t affect the others.
- Per-container resource limits. Container A can be capped at 256 MB, container B at 1 GB.
Microservices Architecture #
Docker is the technical foundation for microservices. Almost no modern microservices architecture doesn’t use containers.
The Problem It Solves #
Monolith applications are hard to scale, slow to deploy, and prone to regressions. A small change in one module requires redeploying the whole application. If one service dies, the entire application goes down.
Microservices split the application into small, independent services. But every small service still needs its own environment, runtime, and deployment. This is where Docker becomes the foundation.
flowchart TB
API[API Gateway]
API --> Auth[auth-service]
API --> User[user-service]
API --> Order[order-service]
API --> Payment[payment-service]
API --> Notif[notification-service]
User --> DBUser[(user-db)]
Order --> DBOrder[(order-db)]
Payment --> DBPay[(payment-db)]Every service in the diagram above is a separate container, with a separate image, separate deployment, and separate scaling.
Benefits #
- Independent deployment. A service can be deployed at any time without coordination.
- Independent scaling. Busy services scale up, quiet ones stay small.
- Failure isolation. A bug or crash in one service doesn’t take down the others.
- Polyglot freedom. Each service can use a different language/runtime without conflicts.
Usually Combined With #
- Kubernetes — multi-host orchestration with self-healing and autoscaling.
- Service meshes (Istio, Linkerd) — observability and policy between services.
- API gateways (Kong, Envoy, NGINX) — centralized routing and authentication.
CI/CD Pipeline #
Docker changes CI/CD from “build on a CI server with configuration X, deploy to a production server with configuration Y” into one pipeline that’s the same from start to finish.
Standard Docker CI/CD Flow #
flowchart LR
A[Git push] --> B[CI Server]
B --> C[docker build]
C --> D[run tests in container]
D --> E{Tests pass?}
E -- No --> F[Fail pipeline]
E -- Yes --> G[docker push to Registry]
G --> H[Deploy to Staging]
H --> I[Smoke test]
I --> J{OK?}
J -- Yes --> K[Deploy to Production]
J -- No --> L[Block + alert]The key point: the image that’s tested = the image that’s deployed. There’s no difference between the build, test, staging, and production environments — they all run the same image.
Example Pipeline (GitHub Actions) #
# .github/workflows/deploy.yml
name: Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t app:${{ github.sha }} .
- name: Run tests
run: |
docker run --rm app:${{ github.sha }} npm test
- name: Push to registry
run: |
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login -u user --password-stdin
docker push app:${{ github.sha }}
- name: Deploy
run: ./deploy.sh app:${{ github.sha }}
Benefits #
- Reproducible builds. The same image can always be rebuilt from the Dockerfile.
- Test environment identical to production. No more “tests pass in CI, fail in production”.
- Standard artifact. Docker images are a universal format — usable on Kubernetes, ECS, Fargate, Cloud Run, or Docker Swarm.
- Fast feedback loop. Image builds are usually < 5 minutes, tests 1–10 minutes, under 15 minutes total per change.
Production Deployment #
Docker images as deployment artifacts are today’s industry standard. The image produced by the CI pipeline can be deployed to many platforms without modification.
Popular Deployment Platforms #
| Platform | Type | Best for |
|---|---|---|
| Kubernetes (EKS, GKE, AKS) | Self-managed orchestrator | Large-scale production |
| Docker Swarm | Docker’s built-in orchestrator | Small clusters, rarely used |
| AWS ECS / Fargate | Managed container service | AWS ecosystem |
| Google Cloud Run | Serverless container | Stateless services, auto-scale to 0 |
| Azure Container Apps | Lightweight managed K8s | Azure ecosystem |
| Fly.io, Railway, Render | Container PaaS | Startups, MVPs, side projects |
| Nomad | Lightweight orchestrator | Kubernetes alternative |
Simple Kubernetes Deployment Example #
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: registry.example.com/web:v1.2.3
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
readinessProbe:
httpGet:
path: /health
port: 8080
Benefits #
- Fast, consistent deployments. The same image to every environment.
- Easy rollback. Just switch the image tag back to the previous version.
- Immutable infrastructure. Servers aren’t modified after deploy — they’re replaced with new containers.
- Auto-scaling. The Horizontal Pod Autoscaler scales containers based on CPU, memory, or custom metrics.
An important production principle: Containers should be stateless. All state (sessions, file uploads, caches) lives outside the container — in a database, Redis, object storage, or volumes. The container itself can be restarted or replaced at any time without losing data.
Legacy Application Modernization #
One of the most undervalued use cases: Docker as a bridge to modernize old applications without a massive rewrite.
The Problem It Solves #
Legacy applications often:
- Run on end-of-life OSes (CentOS 6, Ubuntu 14.04).
- Have dependencies unavailable on modern OSes.
- Use deprecated libraries that can’t be upgraded.
- Have multi-step installs with lost documentation.
Without Docker, the options are: rewrite from scratch (expensive and slow), or keep running old servers (a high security risk).
Solution: Containerization Without Rewriting #
Old applications get wrapped in a container that carries the old OS + old dependencies. This container runs on a modern host without conflicts, and the team gets time to modernize the code gradually.
# Example: PHP 5.6 + Apache + old libraries
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y \
php5 \
php5-mysql \
libapache2-mod-php5 \
&& rm -rf /var/lib/apt/lists/*
COPY ./legacy-app /var/www/html
EXPOSE 80
CMD ["apache2ctl", "-D", "FOREGROUND"]
This container runs fine on an Ubuntu 22.04 or Amazon Linux 2023 host. The legacy application keeps working, and the team can migrate to PHP 8 + a modern framework gradually.
Benefits #
- No need for a big-bang upgrade.
- Minimal change risk for applications that are already stable.
- More time for gradual modernization.
- Extends the application’s life without sacrificing host security.
Databases and Stateful Services #
A hotly debated topic: is it worth running production databases in Docker? The short answer: it depends.
For Development and Testing — Absolutely #
Docker is the fastest way to run a database locally or in CI:
- PostgreSQL for development.
- Redis for integration tests.
- Elasticsearch for experiments.
- MongoDB, MySQL, MariaDB, ClickHouse — all have ready-to-use official images.
# docker-compose.yml for development databases
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: dev
POSTGRES_DB: myapp
volumes:
- pgdata:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
ports:
- "6379:6379"
volumes:
pgdata:
redisdata:
Always use a volume for databases. Data inside a container without a volume will be lost when the container is deleted — this is one of the most common traps. Always mount a volume or named volume for the database’s data directory.
For Production — Be Careful #
Running a production database in Docker is possible and many companies do it, but it requires mature setup:
- Proper storage — local SSD with a good driver (overlay2), or cloud block storage (EBS, Persistent Disk).
- Backup strategy — automated backups to S3/GCS, plus tested restores.
- Monitoring — Prometheus + postgres_exporter, or Datadog/New Relic.
- High availability — replication, automatic failover, or a managed service.
For most teams, a managed database service (RDS, Cloud SQL, Azure Database, Supabase, PlanetScale) is better than self-hosting in Docker — unless you have a dedicated DBA and a strong reason.
Tooling and Utility Containers #
A very useful pattern: “I don’t install the tool, I run the container”.
Instead of installing CLI tools globally on the host, run them via Docker to avoid dependency conflicts and keep the host clean.
Common examples:
# Terraform via Docker
docker run -it --rm -v $PWD:/workspace -w /workspace hashicorp/terraform:1.7 plan
# Hugo (static site generator) via Docker
docker run --rm -v $PWD:/src -p 1313:1313 hugomods/hugo:base server
# FFmpeg for video processing
docker run --rm -v $PWD:/data jrottenberg/ffmpeg -i /data/input.mp4 /data/output.webm
# psql client to query a remote database
docker run -it --rm postgres:16-alpine psql -h db.example.com -U user mydb
# Python REPL with specific libraries
docker run -it --rm python:3.12-slim python
Benefits #
- No globally installed tools — the host stays clean.
- Tool versions always controlled — use specific image tags.
- Reproducible — a script that works today will work 5 years from now if the image tag stays the same.
- Safe for tools with heavy dependencies (e.g. ffmpeg needs libavcodec and friends).
Event-Driven and Background Workers #
Containers are the perfect execution unit for workers processing events from a queue.
Example Architecture #
flowchart LR
API[API Service] --> Q[Message Queue<br/>Kafka / SQS / RabbitMQ]
Q --> W1[Worker 1]
Q --> W2[Worker 2]
Q --> W3[Worker 3]
W1 --> DB[(Database)]
W2 --> DB
W3 --> DBEach worker is a container that:
- Polls the queue for new messages.
- Processes the message (renders images, sends emails, calculates billing, etc.).
- Acknowledges the message when done.
- Crashes and gets restarted automatically by the orchestrator if it fails.
Simple Worker Example #
# worker.py
import boto3
sqs = boto3.client('sqs')
queue_url = 'https://sqs.us-east-1.amazonaws.com/123/my-queue'
while True:
response = sqs.receive_message(QueueUrl=queue_url, MaxNumberOfMessages=1)
for msg in response.get('Messages', []):
process(msg['Body'])
sqs.delete_message(QueueUrl=queue_url, ReceiptHandle=msg['ReceiptHandle'])
# Dockerfile for the worker
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "worker.py"]
Benefits #
- Easy to scale — just add worker replicas when the queue gets long.
- Crash isolation — a bug in one worker doesn’t take down the others.
- Auto-restart — the orchestrator restarts dead workers.
- Clear resource limits — workers can be CPU/memory capped so they don’t disturb other services.
Experimentation and Sandboxing #
Want to try a new language, framework, or database? Docker is the safest and fastest way.
# Try PostgreSQL 17 (still beta)
docker run --rm -d --name pg17 postgres:17beta
# Try Redis with the graph module
docker run --rm -d redis/redis-stack-server
# Try a new web framework
docker run --rm -p 8080:8080 ghcr.io/some-framework/demo
# Benchmark two Python versions
docker run --rm python:3.11 python -c "import time; ..."
docker run --rm python:3.12 python -c "import time; ..."
When you’re done, docker rm -f container and nothing is left behind on the host. Want to try something else? Just docker run again.
Machine Learning and AI Workloads #
Docker is very common in ML/AI workflows:
- Training — run training jobs in containers with specific GPUs and libraries.
- Inference — serve ML models through container APIs (FastAPI, TorchServe, Triton).
- Jupyter Notebook — the
jupyter/datascience-notebookimage gives you a complete environment in one container. - MLOps pipelines — model training → evaluation → packaging → deployment all inside containers.
# docker-compose for ML development
services:
jupyter:
image: jupyter/datascience-notebook
ports:
- "8888:8888"
volumes:
- ./notebooks:/home/jovyan/work
environment:
JUPYTER_ENABLE_LAB: "yes"
mlflow:
image: ghcr.io/mlflow/mlflow
ports:
- "5000:5000"
command: mlflow server --host 0.0.0.0
Edge Computing and IoT #
Lightweight, immutable containers make them ideal for edge devices — small servers in the field, IoT gateways, or retail devices.
Deployment examples:
- Retail kiosks — running a POS application on a Raspberry Pi via containers.
- Industrial gateways — aggregating sensor data and forwarding it to the cloud.
- CDN edge nodes — running proxies/reverse proxies in many geographic locations.
- K8s on the edge — K3s (lightweight Kubernetes) running on edge devices.
Notes for the edge: Images should be small (usealpineordistroless), know how to update themselves (watchtower, k3s auto-upgrade), and tolerate intermittent networks (local caching, exponential backoff retries).
When Docker Is a Poor Fit #
Docker isn’t a cure-all. There are situations where it isn’t the best choice.
Heavy native GUI applications. Desktop applications that need full GPU access, OpenGL, or 3D hardware often have problems in containers. X11 forwarding or VNC can be workarounds, but they’re not seamless.
Extreme high-performance I/O. Databases with very high throughput (hundreds of thousands of IOPS) need kernel and filesystem tuning that sometimes can’t be achieved inside a container. Benchmark before you commit.
Critical stateful workloads without mature orchestration. Production databases without a clear backup strategy, monitoring, and HA will hurt you at the most unexpected time.
Applications heavily dependent on specific kernel modules. Some applications need direct access to kernel modules that aren’t available or are hard to mount in containers.
Environments with very strict regulations. Some industries (defense, classified government work) have rules against containers or shared kernels.
Applications that already work perfectly and won’t be developed further. Don’t re-engineer a stable application. Leave it on VMs or bare metal.
Use Case Summary #
A cheat sheet table for quick decisions:
| Use Case | Docker Fit | Notes |
|---|---|---|
| Local development | ✅ Excellent | Docker Compose recommended |
| Application isolation | ✅ Excellent | The primary solution for dependency conflicts |
| Microservices | ✅ Excellent | The foundation for Kubernetes |
| CI/CD pipeline | ✅ Excellent | Images as the standard artifact |
| Production stateless apps | ✅ Good | Orchestrator recommended |
| Production databases | ⚠️ Be careful | Needs advanced setup or a managed service |
| Legacy app modernization | ✅ Good | Fast & safe solution |
| Tooling / CLI utilities | ✅ Good | “Don’t install, containerize” |
| Event-driven workers | ✅ Excellent | Crash isolation + auto-restart |
| Experimentation & sandboxing | ✅ Excellent | Fast, clean, reproducible |
| ML/AI workloads | ✅ Good | GPU support needs configuration |
| Edge / IoT | ✅ Good | Small images + auto-update |
| GUI desktop apps | ⚠️ Be careful | Needs X11/VNC, not seamless |
| High-performance databases | ⚠️ Benchmark first | I/O tuning may be required |
Summary #
- Docker is a generic platform with many use cases, not a single-purpose tool. Its value comes from image consistency, process isolation, and portability.
- Local development is the use case whose value you feel fastest — onboarding new developers goes from days to minutes.
- Application isolation solves the dependency hell problem that has long been the main source of deployment issues.
- Microservices without containers are nearly impossible at scale. Containers are their technical foundation.
- CI/CD with Docker produces genuinely reproducible pipelines — the image tested = the image deployed.
- Production deployment is best done via an orchestrator (Kubernetes, ECS, Fargate) with images as artifacts.
- Legacy modernization through containerization enables gradual migration without a massive rewrite.
- Production databases in Docker are possible but need mature setup. For most teams, a managed service is more practical.
- Tooling via containers keeps the host clean and tool versions controlled.
- Workers, ML, edge, sandboxing — all strong Docker use cases.
- Docker isn’t for everything. Native GUIs, extreme high-performance I/O, and stable legacy workloads may be better on VMs or bare metal.