What the error means
Compose validates each service against a schema before it does anything. services.web.ports contains an invalid type, it should be a string, number (the exact list of accepted types varies by version) means the value it found under ports for the web service was neither of those — typically because YAML had already turned the port mapping into something else before Compose saw it.
The culprit is YAML 1.1, which many parsers still implement. In that version, 22:22 is a sexagesimal (base-60) integer: 22 × 60 + 22 = 1342. So an unquoted - 22:22 under ports arrives at Compose as the integer 1342, which is not a valid mapping, and the schema check fails. Mappings like 8080:80 and 80:80 survive only because the first number is out of the base-60 digit range on some parsers and not others, which is why the failure looks random.
Why it happens
- An unquoted port mapping with both sides below 60, most famously
22:22for SSH. - A hand-written Compose file converted from a
docker runcommand without quoting the ports. - A templating step that emits the value unquoted even when the source was quoted.
- A parser that applies YAML 1.1 rules, which includes the one used by Compose itself in many versions; YAML 1.2 dropped base-60, but you cannot assume which version you are running.
How to fix it
- Paste the
docker runcommand into the converter above. Every port mapping in the output is quoted, and so is every environment value, for the same reason:DEBUG=truemust stay the string"true", not become a boolean. - In an existing file, add quotes around each entry under
ports:- "22:22". - Quote environment values that YAML could reinterpret: anything that looks like a number, a boolean (
yes,no,on,off,true,false), or a time. - Validate the file with YAML Validator in YAML 1.1 mode, which warns about exactly these ambiguous scalars.
The converted example:
services:
web:
image: nginx:1.27
container_name: web
ports:
- "22:22"
- "80:80"
If it still fails
- The long-form port syntax avoids the problem entirely:
- target: 22,published: 22on separate keys, each a plain integer. - Paste the Compose file into YAML to JSON and look at the
portsarray: if you see1342instead of"22:22", a quote is still missing. - The same base-60 trap bites
version: 3.8(a float, fine) versus a value like10:30in a cron schedule elsewhere in the file.
Related errors
docker: invalid reference format
Image names must be lowercase and come after every flag; put options before the image, fix the tag syntax (name:tag), and check that shell variables in the name are set.
duplicated mapping key
Remove or rename one of the duplicate keys. If both values are needed, they belong under different keys or in a list, not in the same mapping.