Docker Compose vs Dockerfile: What's the Difference?
If you're new to Docker, it's easy to assume a Dockerfile and a compose.yml do the same job. They don't — and mixing them up is usually where "why won't my container start" debugging sessions begin.
A Dockerfile builds one image
A Dockerfile is a recipe for producing a single container image. It starts from a base image, then layers on instructions: copy files in, install packages, run build steps, declare what port the process listens on.
FROM php:8.5-fpm
RUN pecl install xdebug \
&& docker-php-ext-enable xdebug
COPY docker/php/xdebug.ini /usr/local/etc/php/conf.d/docker-xdebug.iniYou build it with docker build -t my-app ., and the result is a single, self-contained image. A Dockerfile has no concept of "other containers" — it doesn't know or care whether a database exists.
Compose orchestrates multiple containers
A compose.yml file is a declarative description of a whole stack: which containers to run, what images or Dockerfiles they come from, how they're networked together, which ports are exposed to your host machine, and what volumes persist data.
services:
nginx:
image: nginx:latest
ports:
- "8080:80"
depends_on:
- php
php:
image: php:8.5-fpm
mysql:
image: mysql:8
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:You run it with docker compose up -d, and Compose starts every service, connects them on a shared network so they can reach each other by service name (php, mysql), and tears them all down together with docker compose down.
The key difference in one line
Dockerfile answers "how is this one image built?" — Compose answers "which containers make up my application, and how do they talk to each other?" They operate at different scopes and solve different problems; neither replaces the other.
When you only need a Dockerfile
If you're publishing a single reusable image — say, a CI runner or a CLI tool distributed via a registry — a Dockerfile alone is enough. There's no orchestration to describe.
When you only need Compose
If your stack is built entirely from off-the-shelf images — nginx, mysql, redis — you don't need a Dockerfile at all. Reference the images directly with image:, as in the example above. This covers most local development setups, including the default output of the generator on this site.
When you need both
You need a Dockerfile plus Compose the moment one of your services requires customization the base image doesn't provide — for example, a PHP image with Xdebug installed. Compose then points at your Dockerfile instead of a plain image:
services:
php:
build:
context: .
dockerfile: docker/php/DockerfileThis is exactly what happens when you enable Xdebug in the generator on this site: it swaps the PHP service from a plain image: reference to a build: block, and generates the accompanying Dockerfile for you.
A common mix-up: build order vs depends_on
Compose's depends_on controls start order between containers — it does not affect how any individual image is built, and it does not wait for a service to be ready (only for it to have started). That's a separate concept, covered in Docker Compose healthchecks explained.
Generate a ready-to-run Docker Compose setup for your stack — compose.yml, nginx config, .env, and a README, in seconds.
Open the generator →