docker/compose · error
--service-ports and --publish are incompatible
Error message
--service-ports and --publish are incompatible
What it means
`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.
Source
Thrown at cmd/compose/run.go:171
createOpts := createOptions{}
buildOpts := buildOptions{
ProjectOptions: p,
}
// We remove the attribute from the option struct and use a dedicated var, to limit confusion and avoid anyone to use options.tty.
// 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.
var ttyFlag bool
cmd := &cobra.Command{
Use: "run [OPTIONS] SERVICE [COMMAND] [ARGS...]",
Short: "Run a one-off command on a service",
Args: cobra.MinimumNArgs(1),
PreRunE: AdaptCmd(func(ctx context.Context, cmd *cobra.Command, args []string) error {
options.Service = args[0]
if len(args) > 1 {
options.Command = args[1:]
}
if len(options.publish) > 0 && options.servicePorts {
return fmt.Errorf("--service-ports and --publish are incompatible")
}
if cmd.Flags().Changed("entrypoint") {
command, err := shellwords.Parse(options.entrypoint)
if err != nil {
return err
}
options.entrypointCmd = command
}
if cmd.Flags().Changed("tty") {
if cmd.Flags().Changed("no-tty") {
return fmt.Errorf("--tty and --no-tty can't be used together")
} else {
options.noTty = !ttyFlag
}
} else if !cmd.Flags().Changed("no-tty") && !cmd.Flags().Changed("interactive") && !dockerCli.In().IsTerminal() {
// while `docker run` requires explicit `-it` flags, Compose enables interactive mode and TTY by default
// but when compose is used from a script that has stdin piped from another command, we just can't
// Here, we detect we run "by default" (user didn't passed explicit flags) and disable TTY allocation ifView on GitHub (pinned to ddc4b044b6)
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.
Example fix
# before docker compose run --service-ports -p 8080:80 web # after docker compose run -p 8080:80 web
Defensive patterns
Strategy: validation
Validate before calling
# bash: exclusive port strategy
if [ ${#PUBLISH[@]} -gt 0 ] && [ "$SERVICE_PORTS" = true ]; then
echo "pick --service-ports OR --publish, not both" >&2; exit 2
fi Prevention
- Model port strategy as one enum (service-ports|explicit) in wrappers.
- Use an override file when both declared and ad-hoc ports are needed.
When it happens
Trigger: Running `docker compose run --service-ports -p 8080:80 web` — any combination where options.publish is non-empty and options.servicePorts is true.
Common situations: 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.
Related errors
- --tty and --no-tty can't be used together
- can't use --progress plain while ANSI support is forced
- --build and --no-build are incompatible
- --force-recreate and --no-recreate are incompatible
- label must be set as KEY=VALUE
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/28fe7e3f3bfeb7d7.
Report an issue: GitHub.