The Compose Development Workflow
With Driftwood fully declared, the day-to-day loop is a handful of docker compose verbs. Bring the stack up and rebuild changed images, tail logs across services, drop into a running container to inspect it, tear everything down — including data when you want a clean slate — and let Compose watch source files and sync or rebuild on change.
This is the inner loop the rest of Layer A was building toward: edit code, see it run, with the whole stack managed as one object. Five verbs cover almost everything you do day to day.
up --buildlogs -fexecwatchdownBring It Up and Rebuild
docker compose up --build builds any service that has a build: — Driftwood's web — before starting, so a Dockerfile or dependency change is actually picked up. A bare up reuses the existing image and streams logs in the foreground; -d detaches and runs the stack in the background. The flag that bites people is --build: leave it off after changing the Dockerfile and Compose happily runs your old image.
Tailing Logs Across Services
docker compose logs -f follows aggregated, color-tagged output from every service at once, and docker compose logs -f web narrows to one. This is how you watch web connect to db and proxy route a request without juggling three separate terminals — the project's output is one interleaved stream, prefixed by service name so you can tell who said what.
Getting Inside a Running Container
docker compose exec web bash opens a shell in the live web container — run a Flask shell, check the environment, hit db with psql over the network. This is distinct from docker compose run, which starts a fresh one-off container that is not the one serving traffic. exec is the everyday debugging entry point because it operates on the actual running instance, with its actual state.
docker compose up --build -d # build web, start the stack detached docker compose logs -f web # follow web's logs docker compose exec web bash # shell into the running web container docker compose down # stop and remove containers + network (keeps volumes) docker compose down -v # also delete named volumes — wipes driftwood-db-data docker compose watch # sync/rebuild on file change
Each verb acts on the whole project, not a single container you named. The one to read twice is the difference between down and down -v — that single flag is the line between a clean restart and data loss.
Tearing Down
docker compose down stops and removes the containers and the default network but keeps named volumes, so the next up finds driftwood-db-data intact and the database survives. docker compose down -v also deletes the named volumes, which wipes driftwood-db-data for a clean-slate reset. In development that -v is a convenient reset; run against data you meant to keep, it is irreversible data loss — the same flag, opposite outcomes.
Live Sync With compose watch
docker compose watch watches the paths you declare and, per rule, either syncs changed files straight into the container or triggers a rebuild — automating the edit-rebuild-restart loop for web so a save is reflected without a manual up --build. A sync rule copies an edited template into the running container instantly; a rebuild rule fires when requirements changes and a fresh image is genuinely needed. It collapses edit-build-restart into edit-and-see.
That is the Layer A finish line. Driftwood is one version-controlled compose.yaml with a dev override, a health-gated startup order, and a five-verb inner loop. The next chapter opens Layer B: the slim driftwood/web image leaves the laptop — tagged, scanned, signed, and pushed to a registry a production host can pull from (Chapter 9).
- Editing the Dockerfile or
requirementsand runningdocker compose upwithout--build— Compose reuses the existing image and your change never makes it in, so you debug stale code. - Running
docker compose down -vto "restart cleanly" and destroyingdriftwood-db-data— the-vdeletes named volumes, wiping the database; in dev it is a reset, against real data it is data loss. - Using
docker compose run web ...for routine debugging instead ofexec—runspins up a separate one-off container with possibly different state, whileexecoperates on the one that is actually serving. - Leaving a foreground
uprunning and assuming the stack is gone after Ctrl-C — Ctrl-C stops it but may leave containers around depending on flags; usedownto actually remove the project.
- Use
docker compose up --build(orcompose watch) after any change to a built service so the running stack reflects the current source, never a stale image. - Reach for
docker compose execto debug the live container and reserverunfor genuine one-off commands, so you inspect the state that is actually serving traffic. - Know that
downpreserves named volumes and onlydown -vdeletes them, and never run-vagainst a stack holding data you intend to keep. - Adopt
docker compose watchfor the inner dev loop onwebso saves sync or rebuild automatically, collapsing edit-build-restart into edit-and-see.
docker run workflow needs separate build, logs, exec, and rm per container
Podman podman-compose mirrors the verbs with thinner watch support
Kubernetes kubectl logs/exec plus Skaffold, Tilt, or Telepresence for live sync
Knowledge Check
Why is --build needed after changing web's Dockerfile or dependencies?
- A bare
upreuses the existing image, so without--buildthe change is never compiled in - Compose always rebuilds every service on
up, so--buildonly changes the log output - Without
--buildCompose pulls the image from a registry instead of using the local one - Without
--buildthe rebuilt service is not attached to the project network and stays isolated
What is the difference between docker compose down and docker compose down -v?
downkeeps named volumes;-valso deletes them, wipingdriftwood-db-datadownonly stops the containers, while the-vflag is what actually removes them-vmakes the whole teardown verbose, printing each step it takes but otherwise changing nothing-vpushes the locally built images up to a registry first, before it removes the running stack
When should you use docker compose exec rather than docker compose run?
- When you need to inspect the container that is actually running and serving traffic
- When you want a fresh, separate one-off container that does not touch the one already running
- When you need to rebuild the service's image from scratch first, before opening a shell into it
- When you want to start up the whole stack of services from inside a single command invocation
What does docker compose watch automate in the dev loop?
- It watches declared paths and, per rule, syncs changed files into the container or triggers a rebuild
- It restarts any service whose healthcheck fails, continuously watching each container's reported health
- It pushes the freshly built image up to a registry automatically every time you commit your code to git
- It follows and tails the aggregated logs of every service in the stack, combined into one live stream
You got correct