{"record":{"id":"28fe7e3f3bfeb7d7","repo":"docker/compose","slug":"service-ports-and-publish-are-incompatible","errorCode":null,"errorMessage":"--service-ports and --publish are incompatible","messagePattern":"--service-ports and --publish are incompatible","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/compose/run.go","lineNumber":171,"sourceCode":"\tcreateOpts := createOptions{}\n\tbuildOpts := buildOptions{\n\t\tProjectOptions: p,\n\t}\n\t// We remove the attribute from the option struct and use a dedicated var, to limit confusion and avoid anyone to use options.tty.\n\t// The tty flag is here for convenience and let user do \"docker compose run -it\" the same way as they use the \"docker run\" command.\n\tvar ttyFlag bool\n\n\tcmd := &cobra.Command{\n\t\tUse:   \"run [OPTIONS] SERVICE [COMMAND] [ARGS...]\",\n\t\tShort: \"Run a one-off command on a service\",\n\t\tArgs:  cobra.MinimumNArgs(1),\n\t\tPreRunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {\n\t\t\toptions.Service = args[0]\n\t\t\tif len(args) > 1 {\n\t\t\t\toptions.Command = args[1:]\n\t\t\t}\n\t\t\tif len(options.publish) > 0 && options.servicePorts {\n\t\t\t\treturn fmt.Errorf(\"--service-ports and --publish are incompatible\")\n\t\t\t}\n\t\t\tif cmd.Flags().Changed(\"entrypoint\") {\n\t\t\t\tcommand, err := shellwords.Parse(options.entrypoint)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\toptions.entrypointCmd = command\n\t\t\t}\n\t\t\tif cmd.Flags().Changed(\"tty\") {\n\t\t\t\tif cmd.Flags().Changed(\"no-tty\") {\n\t\t\t\t\treturn fmt.Errorf(\"--tty and --no-tty can't be used together\")\n\t\t\t\t} else {\n\t\t\t\t\toptions.noTty = !ttyFlag\n\t\t\t\t}\n\t\t\t} else if !cmd.Flags().Changed(\"no-tty\") && !cmd.Flags().Changed(\"interactive\") && !dockerCli.In().IsTerminal() {\n\t\t\t\t// while `docker run` requires explicit `-it` flags, Compose enables interactive mode and TTY by default\n\t\t\t\t// but when compose is used from a script that has stdin piped from another command, we just can't\n\t\t\t\t// Here, we detect we run \"by default\" (user didn't passed explicit flags) and disable TTY allocation if","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/docker/compose/blob/ddc4b044b62e9f715212ea4143fa830fac76382f/cmd/compose/run.go#L153-L189","documentation":"`docker compose run` refuses combining `--service-ports` (publish all ports from the service definition) with explicit `--publish/-p` overrides; the PreRunE check fires as soon as any publish value exists alongside servicePorts.","triggerScenarios":"Running `docker compose run --service-ports -p 8080:80 web` — any combination where options.publish is non-empty and options.servicePorts is true.","commonSituations":"Adding a temporary port override for a one-off run without dropping the habitual --service-ports flag; CI one-off exec commands that stack both flags 'for completeness'; shell aliases including --service-ports.","solutions":["Drop `--service-ports` and keep only the explicit `--publish` mappings you need.","Or drop the `--publish` flags and rely on the service's declared ports via `--service-ports`.","Re-express the desired mapping in the service definition or an override file if you need both sets."],"exampleFix":"# before\ndocker compose run --service-ports -p 8080:80 web\n\n# after\ndocker compose run -p 8080:80 web","handlingStrategy":"validation","validationCode":"# bash: exclusive port strategy\nif [ ${#PUBLISH[@]} -gt 0 ] && [ \"$SERVICE_PORTS\" = true ]; then\n  echo \"pick --service-ports OR --publish, not both\" >&2; exit 2\nfi","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model port strategy as one enum (service-ports|explicit) in wrappers.","Use an override file when both declared and ad-hoc ports are needed."],"tags":["cli","run","ports","flags","conflict"],"backgroundTag":null,"analyzedSha":"ddc4b044b62e9f715212ea4143fa830fac76382f","analyzedAt":"2026-08-15T13:31:42.319Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}