Shell form command in Compose file

It appears that the command override in a Compose service definition will never execute in shell form, only ever exec form. This is contrary to the Compose reference documentation’s statement that “the value can also be a list, in a manner similar to Dockerfile”, as a list in a Dockerfile CMD triggers exec form, while a plain string triggers shell form.

Example compose.yaml:

services:

  exec-form:
    image: alpine:latest
    command: ["/bin/sh", "-c", "echo $$HOSTNAME"]

  shell-form-compose:
    image: alpine:latest
    command: echo $$HOSTNAME

  shell-form-dockerfile:
    build:
      dockerfile_inline: |
        FROM alpine:latest
        CMD echo $$HOSTNAME
    image: alpine:latest

Results (notice the difference between the compose shell form output from the other two):

$ docker compose -f compose.yaml up
[+] Running 3/0
 ✔ Container config-exec-form-1              Created         0.0s 
 ✔ Container config-shell-form-compose-1     Created         0.0s 
 ✔ Container config-shell-form-dockerfile-1  Created         0.0s 


exec-form-1               | 03eae6e24cbf
shell-form-compose-1      | $HOSTNAME
shell-form-dockerfile-1   | 2d7438fcb6a1

I understand that the docs may be ambiguous, but all it says you can define the command as a list. I refers to the syntax. Compose just a client so you can use it instead of multiple docker run commands, but the command in compose is like the arguments of docker run. the arguments will also be arguments of the entrypoint. That is indeed also how exec form works.

Interesting. I thought it’s just a templating issue, but this results in an empty output line:

command: echo $HOSTNAME

If $HOSTNAME does not exist on your host where you run the docker client, the value of a non-existent variable will be empty. $$ is the escaping of $ in compose so it will be interpreted in the Linux container.