You copy a docker-compose.yml from a tutorial, change a couple of values, run docker compose up, and get an error that points at a line that looks perfectly fine. Or worse, no error at all: the stack starts, but one service is quietly missing its port or its environment variables. After half an hour of staring at indentation, it turns out to be a tab character you cannot even see.
Docker Compose files are short, but YAML is unforgiving, and Compose adds its own rules on top. The same handful of mistakes cause most failed stacks. Here they are, with what each one looks like and the exact fix.
Why Compose files break so easily
A Compose file is YAML, a format where indentation carries meaning and a single character can change a value's type. On top of that, Compose expects a specific structure: a services section, each service with an image or a build, and options that must be spelled exactly right. When YAML is valid but the structure is wrong, Compose may ignore the mistake rather than complain, which is how you end up with a container that starts but does not behave.
A quick way to catch most of these before you run anything is to paste the file into the Docker Compose Validator. It parses the YAML properly, reports syntax errors with the line and column, and checks the Compose structure: missing images, misspelled keys, port formats and services named in depends_on that do not exist.
Error 1: Tabs in indentation
YAML only allows spaces for indentation. A single tab, often introduced by an editor setting or by copying from a web page, makes the whole file invalid. The error message usually mentions "found character that cannot start any token", which is not exactly helpful.
Fix: configure your editor to insert spaces, and replace any tabs. Two spaces per level is the Compose convention.
Error 2: Inconsistent indentation
This one is sneakier because the file may still be valid YAML, just not the YAML you meant. If ports is indented one space less than image, it stops belonging to the service and becomes something else entirely.
services:
web:
image: nginx:alpine
ports:
- "8080:80"Fix: keep every option of a service at exactly the same indentation. Most editors can show indentation guides, which make this obvious.
Error 3: Unquoted port mappings
This is the classic Compose trap. YAML 1.1, which many parsers follow, can read some unquoted values containing a colon as numbers in base 60. A mapping like 22:22 can turn into a number instead of the text you wrote, and you get a baffling port error.
Fix: always quote port mappings.
ports:
- "8080:80"
- "2222:22"Error 4: Misspelled keys
imagee, depends_no, enviroment, volume instead of volumes. Depending on your Compose version, an unknown key might produce a clear error, or it might be ignored, leaving you with a service that is missing its configuration.
Fix: check key names carefully, or let a validator do it. The validator compares every service key against the known Compose options, including modern ones like profiles, secrets, configs and deploy, and allows custom x- extension fields.
Error 5: A service with no image and no build
Every service needs to know what to run: either an image to pull or a build context to build from. Leaving both out, often after renaming or refactoring, produces an error that is easy to misread.
Fix: add image: name:tag or build: ./path to the service.
Error 6: depends_on points at a service that does not exist
You rename the db service to postgres but forget that web still says depends_on: [db]. Compose refuses to start.
Fix: make sure every name in depends_on matches a service defined in the same file. Remember that depends_on controls start order, not readiness: your app may still start before the database accepts connections. Use a healthcheck on the database and condition: service_healthy if the order matters.
Error 7: Mixing list and map styles for environment variables
Both of these are valid:
environment:
- POSTGRES_USER=app
- POSTGRES_DB=appenvironment:
POSTGRES_USER: app
POSTGRES_DB: appMixing them inside one block is not. The same applies to labels.
Fix: pick one style per block. The map style is easier to read and edit.
Error 8: The obsolete version key
Many tutorials start with version: '3.8'. Compose v2 no longer uses it and prints a warning that it is obsolete. It will not usually break anything, but it is noise, and it signals that the rest of the example may be out of date too.
Fix: delete the line. The Compose Specification is versionless.
Error 9: Secrets written straight into the file
Not a syntax error, but a mistake that hurts far more: database passwords and API keys written directly under environment, then committed to Git.
Fix: move secrets into an .env file that is not committed, and reference them as variables, or use Docker secrets. If you need a strong random value for a new secret, the Password Generator creates one in your browser.
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}A validator reads ${POSTGRES_PASSWORD} as text; it cannot see your .env file, so variables are resolved only when you run Compose on your machine.
A workflow that saves time
- Paste the file into the Docker Compose Validator and fix everything it reports, top to bottom.
- Run
docker compose configon your machine. It resolves variables, merges override files and shows the final configuration Compose will actually use. - Start the stack with
docker compose upand watch the logs of each service. - Check what you exposed. If a service should be reachable from outside, test the port from another network with a TCP check in the Ping & Uptime Checker.
If you are converting configuration between formats along the way, for example a YAML example into JSON for another tool, the YAML to JSON converter helps you see the structure clearly.
Reading YAML errors without panic
YAML error messages point at where the parser gave up, which is often a line or two after the real mistake. When an error seems to point at a perfectly good line, look just above it: an unclosed quote, a missing colon, or a line indented differently from its neighbours.
It also helps to remember that most Compose problems fall into two groups. Either the file is not valid YAML at all (tabs, indentation, quotes), or it is valid YAML with the wrong shape (missing image, misspelled key, broken depends_on). A validator that checks both saves you from the second group, which is the one that fails silently.
The small habits that prevent most errors
- Indent with two spaces, never tabs.
- Quote every port mapping.
- Keep secrets out of the file and out of Git.
- Delete the
versionline from old examples. - Validate before you run, then let
docker compose confighave the final word.
A Compose file that passes these checks usually just works. And when it does not, you will know the problem is in your application, not in a character you could not see.
If this is part of a wider server setup, our guide to DNS records covers the next step: pointing a domain at the stack you just deployed.




