Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Getting Started with Docker Compose: Bringing Up Bookstore with a Single compose.yaml
Docker Compose defines Bookstore's API, web and SQL Server containers, their network and volume in one compose.yaml and brings them all up together with docker compose up -d. With real output we look at the file's anatomy, build versus image, the project name, DNS by service name, the logs/exec/config commands, what down and down -v do to your data and why the version field is now obsolete.
Service Dependencies: depends_on, Healthcheck Conditions, a Migration Service and Restart
Short depends_on only sets the start order; waiting for a service to be ready needs a health check and condition: service_healthy, and waiting for a one-off job needs service_completed_successfully. We write a health check for SQL Server, move the schema scripts into a migrate service and into Compose 5.3's pre_start init container, and test restart policies and SqlClient retry logic with real outage measurements.
Configuration and Secrets in Compose: environment, env_file, .env Interpolation, secrets and Profiles
.env is the file Compose reads to fill ${VAR} expressions in compose.yaml and it gives nothing to containers; passing environment variables to a container is the job of environment and env_file, passing a password as a file is the job of secrets:. With real output we look at precedence, required variables with ${VAR:?}, moving Bookstore's passwords to /run/secrets, appsettings through configs and optional services with profiles.
A Development Environment: Compose Watch, Hot Reload and Deciding What Runs in a Container
Compose Watch carries host changes into the running container through develop.watch rules: sync copies files and is enough for tools with hot reload, rebuild builds the image again and recreates the container. We compare dependencies-in-Compose/app-in-the-IDE, everything-in-containers and Dev Containers, and look at a 5-second rebuild for Bookstore.Api, sync for the Angular dev server and the delete-permission trap with a non-root user, all with real output.
Multiple Environments: Override Files, include, extends and Merge Rules
In Compose the shared definition lives in compose.yaml and each environment writes only its difference: compose.override.yaml loads automatically when no -f is given, other files are layered with -f; maps merge, while port lists append in unexpected ways. With real output we split Bookstore into a database file with include and a shared sqlcmd definition with extends, and look at !override, !reset and a CI step with a clean tmpfs database, --wait and an exit code.
Compose in Production on a Single Server: Image Tags, Restart, Log Rotation, Resource Limits and the Limits of Compose
The rule for Compose in production on a single server is that the server never builds: the image is built on CI and pushed to a registry with an immutable tag, and the server pulls it with pull and up -d --wait. With real output we look at deploy and rollback for Bookstore, the 11.5-second outage up -d causes through the dependency chain, log rotation, resource limits, an entrypoint that never passes SIGTERM to sqlservr and BACKUP DATABASE as a Compose 5.6 job.
The Evolution of Compose: From docker-compose v1 to Compose v2 and v5, the Compose Specification and 2024–2026 Features
Today's Compose implements a single schema, the Compose Specification: the version field in old files is ignored, fields from the 2.x and 3.x formats are valid together, and v5 continues v2 with a Go SDK and builds handed to Bake. We run Bookstore's 2019 docker-compose.yml with docker-compose 1.25.5, 1.29.2 and Compose 5.7.1, look at the differences with real output, and map the 2024–2026 features and a migration checklist.