skip to Main Content

I want to run a multiline command when starting a container in docker compose:

services:
  foo:
    image: alpine:edge
    entrypoint: ["/bin/sh", "-c"]
    command: >
      apk add --no-cache curl

Running docker compose up gives me the following errors:

[+] Running 2/0
 ✔ Network tmps6nvvhf0ms_default  Created                                                                                                                                                 0.1s
 ✔ Container tmps6nvvhf0ms-foo-1  Created                                                                                                                                                 0.0s
Attaching to foo-1
foo-1  | apk-tools 2.14.0, compiled for aarch64.
foo-1  |
foo-1  | usage: apk [<OPTIONS>...] COMMAND [<ARGUMENTS>...]
foo-1  |
foo-1  | Package installation and removal:
foo-1  |   add        Add packages to WORLD and commit changes
foo-1  |   del        Remove packages from WORLD and commit changes
...
...

It’s clear that only the first word apk was passed to docker compose. I tried with other commands and they have the same results.

docker version:

Client: Docker Engine - Community
 Version:           24.0.7
 API version:       1.43
 Go version:        go1.21.3
 Git commit:        afdd53b4e3
 Built:             Thu Oct 26 07:06:42 2023
 OS/Arch:           darwin/arm64
 Context:           colima

Server: Docker Engine - Community
 Engine:
  Version:          24.0.7
  API version:      1.43 (minimum version 1.12)
  Go version:       go1.20.10
  Git commit:       311b9ff
  Built:            Thu Oct 26 09:08:29 2023
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.6.25
  GitCommit:        d8f198a4ed8892c764191ef7b3b06d8a2eeb5c7f
 runc:
  Version:          1.1.10
  GitCommit:        v1.1.10-0-g18a0cb0
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

docker compose version

Docker Compose version 2.23.3

3

Answers


  1. Because you specify the command as a string, it is subjected to shell splitting. Given the fragment in your question, you end up with the following invocation:

    /bin/sh -c apk add --no-cache curl
    

    Whereas you meant:

    /bin/sh -c "apk add --no-cache curl"
    

    One way to accomplish that is by specifying an array for command, which disables shell splitting:

    services:
      foo:
        command:
          - apk add --no-cache curl
    
    Login or Signup to reply.
  2. As @Botje indicates in their answer, Compose splits a multi-word command: into separate words for you, but sh -c only expects a single word as its input.

    I’d address this by just deleting the entrypoint: override. The YAML block scalar (multiline string) syntax isn’t special here.

    services:
      foo:
        image: alpine:edge
        command: apk add --no-cache curl
    

    Note that the command: gets repeated every time you start the container. It’s not usually a good practice to install software inside a running container, since that will be lost as soon as the container exits. A better approach is to do the installation once as part of building a custom image. The Compose file becomes even simpler

    services:
      foo:
        build: .
        # no image:, entrypoint:, command:
    

    and in the same directory, a Dockerfile

    FROM alpine:edge             # was Compose image:
    RUN apk add --no-cache curl  # was Compose command:
    CMD ???                      # what should the container _actually_ do?
    

    This also has the advantage of it being easy to have multiple RUN lines, which run in order, and of the RUN lines actually being processed by a shell.

    Login or Signup to reply.
  3. Alternatively, this form is convenient:

    services:
      foo:
        image: alpine:edge
        command: 
        - /bin/sh
        - -c
        - |
          apk add --no-cache curl
          curl http://example.com
          
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search