Docker Compose for Beginners: From docker run to docker-compose.yml
If you have ever run a docker run command with 10 flags and forgotten what half of them do the next day, Docker Compose is your solution. This guide walks you through converting messy run commands into clean, version-controlled YAML files.
Why Docker Compose?
A typical development setup might need a web server, a database, a cache layer, and a background worker. Running each as a separate docker run command means:
- Remembering port mappings for every service
- Manually creating networks so containers can talk
- Retyping volume mounts every time
- No version control for your infrastructure
Docker Compose solves all of this with a single docker-compose.yml file. One command — docker compose up — brings your entire stack online.
Anatomy of a docker-compose.yml
version: "3.8"
services:
web:
image: node:18-alpine
ports:
- "3000:3000"
volumes:
- ./src:/app/src
environment:
- NODE_ENV=development
depends_on:
- db
db:
image: postgres:15
ports:
- "5432:5432"
environment:
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=myapp
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:Converting docker run to Compose
Here is a real-world example. This docker run command:
docker run -d \
--name myapp \
-p 3000:3000 \
-v ./data:/app/data \
-e DATABASE_URL=postgres://localhost/db \
--restart always \
node:18-alpine npm startBecomes this in docker-compose.yml:
services:
myapp:
image: node:18-alpine
container_name: myapp
ports:
- "3000:3000"
volumes:
- ./data:/app/data
environment:
- DATABASE_URL=postgres://localhost/db
restart: always
command: npm startThe mapping is straightforward:
-pbecomesports:-vbecomesvolumes:-ebecomesenvironment:--namebecomescontainer_name:--restartbecomesrestart:- The trailing command becomes
command:
Common Patterns
Multi-service with networking
Services in the same Compose file can reach each other by service name. No need to manually create networks or use container IPs.
services:
api:
image: myapi:latest
environment:
- REDIS_URL=redis://cache:6379
cache:
image: redis:7-alpineThe api service connects to Redis using cache as the hostname — Docker Compose DNS handles it automatically.
Environment files
Instead of listing 20 environment variables inline, use an env file:
services:
api:
image: myapi:latest
env_file:
- .env.localHealth checks
services:
db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5Essential Commands
docker compose up -d— start all services in backgrounddocker compose down— stop and remove containersdocker compose logs -f web— follow logs for one servicedocker compose exec web sh— shell into a running containerdocker compose build— rebuild imagesdocker compose ps— list running services
Tips
- Always pin image versions — use
postgres:15notpostgres:latest - Use named volumes for database data — anonymous volumes get deleted on
down - Add
.dockerignore— keep node_modules and .git out of build context - Use
depends_onfor startup ordering — but note it does not wait for readiness, only container start
Automate the conversion
Do not want to translate flags manually? Use our Docker Run to Compose tool — paste any docker run command and get a ready-to-use docker-compose.yml instantly.
Related tools: