Skip to content

[BUG] docker compose does not start newly built image when COMPOSE_BAKE is true #12774

Closed

Description

@Paraphraser

Description

Running docker compose up -d --build «container» in the presence of COMPOSE_BAKE=true does not re-create the container with the newly-built image. Removing COMPOSE_BAKE from the environment returns to the original behaviour.

Steps To Reproduce

Scenario

I am using an instance of a customised MariaDB container intended to be the back-end for a Gitea container. Although I haven't tested with different base images, I don't believe that the base image is relevant. MariaDB is simply where I noticed this problem and am able to demonstrate it easily.

Instrumentation

Instrumentation used to demonstrate problem:

  • Health-check script includes:

     FORCE_CHANGE=1
     echo "Forced change marker = $FORCE_CHANGE"
  • Dockerfile includes:

     RUN date >/builddate.txt

    This is positioned after the health-check script is copied into the image.

Runtime environment

$ echo $COMPOSE_BAKE
true
$

Test 1

  1. Show container not running:

    $ docker top gitea_db
    Error response from daemon: No such container: gitea_db
  2. Build container:

    $ docker compose up -d --build gitea_db
    [+] Building 0.5s (11/11) FINISHED                                                                                                                              
     => [internal] load local bake definitions                                                                                                                 0.0s
     => => reading from stdin 382B                                                                                                                             0.0s
     => [internal] load build definition from Dockerfile                                                                                                       0.0s
     => => transferring dockerfile: 1.16kB                                                                                                                     0.0s
     => [internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                                                        0.0s
     => [internal] load .dockerignore                                                                                                                          0.0s
     => => transferring context: 2B                                                                                                                            0.0s
     => [1/4] FROM ghcr.io/linuxserver/mariadb:latest                                                                                                          0.0s
     => [internal] load build context                                                                                                                          0.0s
     => => transferring context: 45B                                                                                                                           0.0s
     => CACHED [2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak         -e "s/^thread_cache_siz  0.0s
     => CACHED [3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                                                       0.0s
     => CACHED [4/4] RUN date >/builddate.txt                                                                                                                  0.0s
     => exporting to image                                                                                                                                     0.0s
     => => exporting layers                                                                                                                                    0.0s
     => => writing image sha256:e66432205ec39196c2cac3dde2391bbe741af5906b26ba8508127ff3af49f56c                                                               0.0s
     => => naming to docker.io/library/iotstack-gitea_db                                                                                                       0.0s
     => resolving provenance for metadata file                                                                                                                 0.0s
    [+] Running 3/3
     ✔ gitea_db                    Built                                                                                                                       0.0s 
     ✔ Network iotstack_nextcloud  Created                                                                                                                     0.1s 
     ✔ Container gitea_db          Started                                                                                                                     0.6s 
    $ 
  3. Probe instrumentation:

    $ docker exec gitea_db cat /builddate.txt
    Thu Apr 24 01:32:18 UTC 2025
    
    $ docker exec gitea_db iotstack_healthcheck.sh
    Forced change marker = 1

    This is the baseline.

  4. Bump counter in health-check script:

    FORCE_CHANGE=2
    echo "Forced change marker = $FORCE_CHANGE"
  5. Rebuild container:

    $ docker compose up -d --build gitea_db
    [+] Building 0.5s (11/11) FINISHED                                                                                                                              
     => [internal] load local bake definitions                                                                                                                 0.0s
     => => reading from stdin 382B                                                                                                                             0.0s
     => [internal] load build definition from Dockerfile                                                                                                       0.0s
     => => transferring dockerfile: 1.16kB                                                                                                                     0.0s
     => [internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                                                        0.0s
     => [internal] load .dockerignore                                                                                                                          0.0s
     => => transferring context: 2B                                                                                                                            0.0s
     => [1/4] FROM ghcr.io/linuxserver/mariadb:latest                                                                                                          0.0s
     => [internal] load build context                                                                                                                          0.0s
     => => transferring context: 1.08kB                                                                                                                        0.0s
     => CACHED [2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak         -e "s/^thread_cache_siz  0.0s
     => CACHED [3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                                                       0.0s
     => CACHED [4/4] RUN date >/builddate.txt                                                                                                                  0.0s
     => exporting to image                                                                                                                                     0.0s
     => => exporting layers                                                                                                                                    0.0s
     => => writing image sha256:a59aa5c348501b84089dd38195edddb1167175750fc0f7b189f216e3b5faa7ac                                                               0.0s
     => => naming to docker.io/library/iotstack-gitea_db                                                                                                       0.0s
     => resolving provenance for metadata file                                                                                                                 0.0s
    [+] Running 2/2
     ✔ gitea_db            Built                                                                                                                               0.0s 
     ✔ Container gitea_db  Running                                                                                                                             0.0s 

    Note that container is marked "running" rather than being re-created.

  6. Probe instrumentation:

    $ docker exec gitea_db cat /builddate.txt
    Thu Apr 24 01:32:18 UTC 2025
    
    $ docker exec gitea_db iotstack_healthcheck.sh
    Forced change marker = 1

    No change from baseline so this is still the prior image that is running.

  7. Send another explicit "up":

    $ docker compose up -d gitea_db
    [+] Running 1/1
     ✔ Container gitea_db  Started                                                                                                                             4.1s

    Container "Started", as distinct from "Running" at the end of step 5.

  8. Probe instrumentation:

    $ docker exec gitea_db cat /builddate.txt
    Thu Apr 24 01:30:47 UTC 2025
    
    $ docker exec gitea_db iotstack_healthcheck.sh
    Forced change marker = 2

    Newly-built image now running.

Revised runtime environment

$ echo $COMPOSE_BAKE

$

Test 2

  1. Create initial conditions (steps 1..3 of Test 1):

    $ docker top gitea_db
    Error response from daemon: No such container: gitea_db
    
    $ docker compose up -d --build gitea_db
    Compose can now delegate builds to bake for better performance.
     To do so, set COMPOSE_BAKE=true.
    [+] Building 0.9s (10/10) FINISHED                                                                                                               docker:default
     => [gitea_db internal] load build definition from Dockerfile                                                                                              0.0s
     => => transferring dockerfile: 1.16kB                                                                                                                     0.0s
     => [gitea_db internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                                               0.0s
     => [gitea_db internal] load .dockerignore                                                                                                                 0.0s
     => => transferring context: 2B                                                                                                                            0.0s
     => [gitea_db internal] load build context                                                                                                                 0.0s
     => => transferring context: 1.08kB                                                                                                                        0.0s
     => [gitea_db 1/4] FROM ghcr.io/linuxserver/mariadb:latest                                                                                                 0.0s
     => CACHED [gitea_db 2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak         -e "s/^thread_  0.0s
     => [gitea_db 3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                                                     0.1s
     => [gitea_db 4/4] RUN date >/builddate.txt                                                                                                                0.5s
     => [gitea_db] exporting to image                                                                                                                          0.1s
     => => exporting layers                                                                                                                                    0.1s
     => => writing image sha256:3d783b11bca0626cb88cb08ad48f16f16db5a662e145bc94e68058b1d0cf0f17                                                               0.0s
     => => naming to docker.io/library/iotstack-gitea_db                                                                                                       0.0s
     => [gitea_db] resolving provenance for metadata file                                                                                                      0.0s
    [+] Running 3/3
     ✔ gitea_db                    Built                                                                                                                       0.0s 
     ✔ Network iotstack_nextcloud  Created                                                                                                                     0.1s 
     ✔ Container gitea_db          Started                                                                                                                     0.5s 
    
    $ docker exec gitea_db cat /builddate.txt
    Thu Apr 24 01:51:42 UTC 2025
    
    $ docker exec gitea_db iotstack_healthcheck.sh
    Forced change marker = 3
  2. Bump forced-change marker to 4 and rebuild:

    $ docker compose up -d --build gitea_db
    Compose can now delegate builds to bake for better performance.
     To do so, set COMPOSE_BAKE=true.
    [+] Building 0.9s (10/10) FINISHED                                                                                                               docker:default
     => [gitea_db internal] load build definition from Dockerfile                                                                                              0.0s
     => => transferring dockerfile: 1.16kB                                                                                                                     0.0s
     => [gitea_db internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                                               0.0s
     => [gitea_db internal] load .dockerignore                                                                                                                 0.0s
     => => transferring context: 2B                                                                                                                            0.0s
     => [gitea_db 1/4] FROM ghcr.io/linuxserver/mariadb:latest                                                                                                 0.0s
     => [gitea_db internal] load build context                                                                                                                 0.0s
     => => transferring context: 1.08kB                                                                                                                        0.0s
     => CACHED [gitea_db 2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak         -e "s/^thread_  0.0s
     => [gitea_db 3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                                                     0.0s
     => [gitea_db 4/4] RUN date >/builddate.txt                                                                                                                0.5s
     => [gitea_db] exporting to image                                                                                                                          0.1s
     => => exporting layers                                                                                                                                    0.1s
     => => writing image sha256:9bdb11df90af862a04e93887dc7ffc32f90271825a5eb5e54ac52c6b6ccdc979                                                               0.0s
     => => naming to docker.io/library/iotstack-gitea_db                                                                                                       0.0s
     => [gitea_db] resolving provenance for metadata file                                                                                                      0.0s
    [+] Running 2/2
     ✔ gitea_db            Built                                                                                                                               0.0s 
     ✔ Container gitea_db  Started                                                                                                                             4.0s 
    
    $ docker exec gitea_db cat /builddate.txt
    Thu Apr 24 01:52:29 UTC 2025
    
    $ docker exec gitea_db iotstack_healthcheck.sh
    Forced change marker = 4

    This time the -d --build resulted in the newly-built image being instantiated as the running container.

The only difference between tests 1 and 2 is COMPOSE_BAKE.

Compose Version

$ docker version -f "{{.Server.Version}}"
28.1.1

$ docker compose version
Docker Compose version v2.35.1

$ docker-compose version
Docker Compose version v2.35.1

Docker Environment

Client: Docker Engine - Community
 Version:    28.1.1
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc.)
    Version:  v0.23.0
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx
  compose: Docker Compose (Docker Inc.)
    Version:  v2.35.1
    Path:     /usr/libexec/docker/cli-plugins/docker-compose

Server:
 Containers: 8
  Running: 8
  Paused: 0
  Stopped: 0
 Images: 20
 Server Version: 28.1.1
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: local
 Cgroup Driver: systemd
 Cgroup Version: 2
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
 Swarm: inactive
 Runtimes: io.containerd.runc.v2 runc
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 05044ec0a9a75232cad458027ca83437aae3f4da
 runc version: v1.2.5-0-g59923ef
 init version: de40ad0
 Security Options:
  seccomp
   Profile: builtin
  cgroupns
 Kernel Version: 6.1.21-v8+
 Operating System: Debian GNU/Linux 11 (bullseye)
 OSType: linux
 Architecture: aarch64
 CPUs: 4
 Total Memory: 3.704GiB
 Name: sec-dev
 ID: f6406dbb-2fc1-4834-9606-5e826871aec6
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Experimental: false
 Insecure Registries:
  ::1/128
  127.0.0.0/8
 Live Restore Enabled: false

Anything else?

No response

Activity

  1. self-assigned this
    on Apr 24, 2025
  2. ndeloof commented on Apr 24, 2025

    @ndeloof
    Contributor

    step 5 shows all build stepped resolved from CACHE, so image hasn't changed after the build.
    can you pease try running docker buildx bake -f compose.yaml in same conditions, to double check buildx detect healtcheck script has been changed and image need to be rebuilt ?

  3. Paraphraser commented on Apr 25, 2025

    @Paraphraser
    Author

    I noticed that the --build in the presence of COMPOSE_BAKE=true resulted in CACHED but the test sequence also shows the subsequent up where it is clear that the newly-built image does reflect the change to the health-check script.

    In other words, given that it is possible to prove that the health-check script has changed, it seems to me that any log line reporting CACHED is a bare-faced lie. Do you disagree?

    I have done as you asked but I am not sure what it has achieved.

    1. Establish baseline conditions:

      $ HEALTH_SCRIPT=./.templates/mariadb/iotstack_healthcheck.sh
      
      $ ls -l $HEALTH_SCRIPT
      -rwxr-xr-x 1 pi pi 1029 Apr 25 10:28 ./.templates/mariadb/iotstack_healthcheck.sh
      
      $ sha256 $HEALTH_SCRIPT
      7a4aa23bd490e1d9d71343f560dd174bfdb4661a1b7877f5532361f51a7fc715
      
      $ grep "^FORCE_CHANGE" $HEALTH_SCRIPT
      FORCE_CHANGE=20
      
      $ docker top gitea_db
      Error response from daemon: No such container: gitea_db
      
      $ docker compose up -d --build gitea_db
      Compose can now delegate builds to bake for better performance.
       To do so, set COMPOSE_BAKE=true.
      [+] Building 0.8s (10/10) FINISHED                                                                                                               docker:default
       => [gitea_db internal] load build definition from Dockerfile                                                                                              0.0s
       => => transferring dockerfile: 1.16kB                                                                                                                     0.0s
       => [gitea_db internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                                               0.0s
       => [gitea_db internal] load .dockerignore                                                                                                                 0.0s
       => => transferring context: 2B                                                                                                                            0.0s
       => [gitea_db 1/4] FROM ghcr.io/linuxserver/mariadb:latest                                                                                                 0.0s
       => [gitea_db internal] load build context                                                                                                                 0.0s
       => => transferring context: 1.08kB                                                                                                                        0.0s
       => CACHED [gitea_db 2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak         -e "s/^thread_  0.0s
       => [gitea_db 3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                                                     0.1s
       => [gitea_db 4/4] RUN date >/builddate.txt                                                                                                                0.5s
       => [gitea_db] exporting to image                                                                                                                          0.1s
       => => exporting layers                                                                                                                                    0.1s
       => => writing image sha256:b06cc5186319671a143dad5eeab6de8491ae4a2e41136b8eb50ce413a316bc49                                                               0.0s
       => => naming to docker.io/library/iotstack-gitea_db                                                                                                       0.0s
       => [gitea_db] resolving provenance for metadata file                                                                                                      0.0s
      [+] Running 3/3
       ✔ gitea_db                    Built                                                                                                                       0.0s 
       ✔ Network iotstack_nextcloud  Created                                                                                                                     0.1s 
       ✔ Container gitea_db          Started                                                                                                                     0.5s 
      
      $ docker exec gitea_db cat /builddate.txt
      Fri Apr 25 00:29:06 UTC 2025
      
      $ docker exec gitea_db iotstack_healthcheck.sh
      Forced change marker = 20
    2. Change health-check file and prove it has changed:

      $ ls -l $HEALTH_SCRIPT
      -rwxr-xr-x 1 pi pi 1029 Apr 25 10:29 ./.templates/mariadb/iotstack_healthcheck.sh
      
      $ sha256 $HEALTH_SCRIPT
      17f54764ddd4d3345079846477a45d7fdc5507b7fea8a1dbaaefda6067ca0023
      
      $ grep "^FORCE_CHANGE" $HEALTH_SCRIPT
      FORCE_CHANGE=21
    3. Run requested command:

      $ docker buildx bake -f docker-compose.yml gitea_db
      [+] Building 1.2s (10/10) FINISHED                                                                                                               docker:default
       => [internal] load local bake definitions                                                                                                                 0.0s
       => => reading docker-compose.yml 11.31kB / 11.31kB                                                                                                        0.0s
       => [internal] load build definition from Dockerfile                                                                                                       0.0s
       => => transferring dockerfile: 1.16kB                                                                                                                     0.0s
       => [internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                                                        0.0s
       => [internal] load .dockerignore                                                                                                                          0.0s
       => => transferring context: 2B                                                                                                                            0.0s
       => [1/4] FROM ghcr.io/linuxserver/mariadb:latest                                                                                                          0.0s
       => [internal] load build context                                                                                                                          0.0s
       => => transferring context: 1.08kB                                                                                                                        0.0s
       => CACHED [2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak         -e "s/^thread_cache_siz  0.0s
       => [3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                                                              0.0s
       => [4/4] RUN date >/builddate.txt                                                                                                                         0.5s
       => exporting to image                                                                                                                                     0.1s
       => => exporting layers                                                                                                                                    0.1s
       => => writing image sha256:90b90ec1f48dca59adda197cd5a9b98c94ae3feae0aa858bdfa205b71ecf1388                                                               0.0s

      The COPY and RUN date steps aren't cached so it would seem that this process is aware of the change to the health-check script.

    4. But, wherever it is that the buildx bake command wrote the image, that image isn't auto-instantiated:

      $ docker exec gitea_db cat /builddate.txt
      Fri Apr 25 00:29:06 UTC 2025
      
      $ docker exec gitea_db iotstack_healthcheck.sh
      Forced change marker = 20
    5. And, unlike the original test, the new image isn't available to docker compose up either:

      $ docker compose up -d gitea_db
      [+] Running 1/1
       ✔ Container gitea_db  Running                                                                                                                             0.0s 
      
      $ docker exec gitea_db cat /builddate.txt
      Fri Apr 25 00:29:06 UTC 2025
      
      $ docker exec gitea_db iotstack_healthcheck.sh
      Forced change marker = 20
    6. The new image doesn't even get instantiated on a re-create:

      $ docker compose up -d --force-recreate gitea_db
      [+] Running 1/1
       ✔ Container gitea_db  Started                                                                                                                             4.1s 
      
      $ docker exec gitea_db cat /builddate.txt
      Fri Apr 25 00:29:06 UTC 2025
      
      $ docker exec gitea_db iotstack_healthcheck.sh
      Forced change marker = 20
      

    So, if I can, please, I'd really like to come back to the original point of the issue which is that, given these two assumptions:

    1. The basic command:

      $ docker compose up -d --build «container»
      
    2. The provable situation where the health-check script has changed,

    then, if COMPOSE_BAKE is:

    • true, the build output falsely reports that the health-check script is cached, whereas a second "up" proves that the newly-built image does incorporate the change;
    • omitted, the build output correctly reports that the health-check script is copied, the resulting image is instantiated without needing a second "up" and does incorporate the change.

    I have no idea what buildx bake does. I've never used it until today. I have no idea where it places the built image nor why that isn't available to an "up" command. I apologise if I am being obtuse but it seems to me that all this test has really shown is an awareness by buildx bake that the health-check script has changed.

    But how this helps get to the basic problem of the docker compose --build flag misbehaving in the presence of COMPOSE_BAKE is unclear. I must be missing something.

  4. ndeloof commented on Apr 25, 2025

    @ndeloof
    Contributor

    I must be missing something

    With COMPOSE_BAKE variable we prepare Docker Compose future to drop internal implementation of image builder, and fully delegate the image build process to Bake, which is dedicated to this topic.

    My request to run docker buildx bake ... is to double check compose configures bake correctly as you run COMPOSE_BAKE=true docker compose build. A surprising point is that you get a successful build, which seems to demonstrates changes have been introduced in the image (COPY iotstack_healthcheck.sh is not tagged as CACHED) still docker compose up doesn't detect image has been updated.

    Can you please check image digest before and after bake build ? (docker image inspect <image> -f '{{.ID}}'), which is used by compose to detect container must be recreated (can compare with running container label com.docker.compose.image)

  5. Paraphraser commented on Apr 25, 2025

    @Paraphraser
    Author

    Maybe this will assist.

    In words:

    1. Variable for easy reference to the only thing that is changing (the health-check script).
    2. Date/time of script.
    3. Hash of script.
    4. Key line of script.
    5. No containers running.
    6. No images whatsoever.
    7. Run docker buildx bake which has to download the base image (ghcr.io/linuxserver/mariadb).
    8. Show one untagged image has appeared.
    9. Inspect that image as per your request.
    $ HEALTH_SCRIPT=./.templates/mariadb/iotstack_healthcheck.sh
    
    $ ls -l $HEALTH_SCRIPT
    -rwxr-xr-x 1 pi pi 1043 Apr 25 23:39 ./.templates/mariadb/iotstack_healthcheck.sh
    
    $ sha256 $HEALTH_SCRIPT
    a4917b05a8199330ba27542a878175249f50320b4736c77cb6b6f8d056cec818
    
    $ grep "^FORCE_CHANGE" $HEALTH_SCRIPT
    FORCE_CHANGE=thirdTimeCharmed
    
    $ docker ps
    CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
    
    $ docker images
    REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
    
    $ docker buildx bake -f docker-compose.yml gitea_db
    [+] Building 24.8s (10/10) FINISHED                                                                          docker:default
     => [internal] load local bake definitions                                                                             0.0s
     => => reading docker-compose.yml 598B / 598B                                                                          0.0s
     => [internal] load build definition from Dockerfile                                                                   0.0s
     => => transferring dockerfile: 1.16kB                                                                                 0.0s
     => [internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                    3.1s
     => [internal] load .dockerignore                                                                                      0.0s
     => => transferring context: 2B                                                                                        0.0s
     => [1/4] FROM ghcr.io/linuxserver/mariadb:latest@sha256:43528387250c2c65e511ef53d42a13c30fa2b03b9d1e757b6ba4aa8fc86  19.1s
     => => resolve ghcr.io/linuxserver/mariadb:latest@sha256:43528387250c2c65e511ef53d42a13c30fa2b03b9d1e757b6ba4aa8fc86b  0.0s
     => => sha256:e47781d989225cea7d925e1cee98a131d59451751e3907d4a5906ce5e41a8d0c 2.00kB / 2.00kB                         0.0s
     => => sha256:308a42fc9ef243b8744c850891b63f6bb7ca05bc51a4107d292c523d32d7460c 6.87MB / 6.87MB                         1.5s
     => => sha256:e1cde46db0e1399e7c13610262617155cd0a3e7823f951b9870fbc36a772039b 6.21kB / 6.21kB                         0.8s
     => => sha256:43528387250c2c65e511ef53d42a13c30fa2b03b9d1e757b6ba4aa8fc86b050c 1.61kB / 1.61kB                         0.0s
     => => sha256:34f62766cc99ba8e5e64d8960ee893a5d2271eedbe86dd8701754008d7c464c1 8.57kB / 8.57kB                         0.0s
     => => sha256:8045a3329b93c18f962c1e70b064b580704f8b4b0d7ad2636d9913677d1bfe9e 1.36kB / 1.36kB                         0.5s
     => => sha256:992215a09f7831a9b9cb84a80604d30597b75a9e3f7754f5277f865647ef1718 701B / 701B                             1.0s
     => => sha256:858664fe3387a0fb76b3a16317bceb87fd2a3df44e57db48fc90bf6bf137cc24 428B / 428B                             1.3s
     => => sha256:f80ae8fdc5c3446495a2beb1c12a5bd9e0910f27771abab77360b1e3d86b2108 6.15MB / 6.15MB                         2.0s
     => => sha256:0c6d2e5dc8beab8776ad12e3d186055eba99b5a0745185169c9bc120b01f0774 3.97kB / 3.97kB                         1.8s
     => => extracting sha256:308a42fc9ef243b8744c850891b63f6bb7ca05bc51a4107d292c523d32d7460c                              1.2s
     => => sha256:37627924acafddaf30b66d4396d6cf1418968cd3cfd0461ec365573429f777f7 90.47MB / 90.47MB                       8.3s
     => => sha256:2c0cc03425418562ecda976236b6b13183d3b975d8f39607f85d24f1eccbd1e0 6.00kB / 6.00kB                         2.4s
     => => extracting sha256:e1cde46db0e1399e7c13610262617155cd0a3e7823f951b9870fbc36a772039b                              0.0s
     => => extracting sha256:8045a3329b93c18f962c1e70b064b580704f8b4b0d7ad2636d9913677d1bfe9e                              0.0s
     => => extracting sha256:992215a09f7831a9b9cb84a80604d30597b75a9e3f7754f5277f865647ef1718                              0.0s
     => => extracting sha256:858664fe3387a0fb76b3a16317bceb87fd2a3df44e57db48fc90bf6bf137cc24                              0.0s
     => => extracting sha256:f80ae8fdc5c3446495a2beb1c12a5bd9e0910f27771abab77360b1e3d86b2108                              1.1s
     => => extracting sha256:0c6d2e5dc8beab8776ad12e3d186055eba99b5a0745185169c9bc120b01f0774                              0.0s
     => => extracting sha256:37627924acafddaf30b66d4396d6cf1418968cd3cfd0461ec365573429f777f7                             10.1s
     => => extracting sha256:2c0cc03425418562ecda976236b6b13183d3b975d8f39607f85d24f1eccbd1e0                              0.0s
     => [internal] load build context                                                                                      0.0s
     => => transferring context: 1.09kB                                                                                    0.0s
     => [2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak     1.5s
     => [3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                          0.1s
     => [4/4] RUN date >/builddate.txt                                                                                     0.5s
     => exporting to image                                                                                                 0.1s
     => => exporting layers                                                                                                0.1s
     => => writing image sha256:be00015b417c728499704fb19254742c6c689af9c9cb0cfe93b6f220d7518278                           0.0s
    
    $ docker images
    REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
    <none>       <none>    be00015b417c   47 seconds ago   354MB
    
    $ docker image inspect be00015b417c -f '{{.ID}}'
    sha256:be00015b417c728499704fb19254742c6c689af9c9cb0cfe93b6f220d7518278

    health-check script changed.

    1. Date/time of script.
    2. Hash of script.
    3. Key line of script.
    4. Run docker buildx bake which should (and does) sense the change to the health-check script.
    5. Show an additional untagged image has appeared.
    6. Inspect that second image as per your request.
    7. Show no running container instantiated as a result of all of that.
    $ ls -l $HEALTH_SCRIPT
    -rwxr-xr-x 1 pi pi 1042 Apr 25 23:43 ./.templates/mariadb/iotstack_healthcheck.sh
    
    $ sha256 $HEALTH_SCRIPT
    f37621ca0ac790c3735cc8f5ae8cfa00c43d4d523df4b2f333ceadeb7be59f6c
    
    $ grep "^FORCE_CHANGE" $HEALTH_SCRIPT
    FORCE_CHANGE=fourWithFeeling
    
    $ docker buildx bake -f docker-compose.yml gitea_db
    [+] Building 1.8s (10/10) FINISHED                                                                           docker:default
     => [internal] load local bake definitions                                                                             0.0s
     => => reading docker-compose.yml 598B / 598B                                                                          0.0s
     => [internal] load build definition from Dockerfile                                                                   0.0s
     => => transferring dockerfile: 1.16kB                                                                                 0.0s
     => [internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                    0.8s
     => [internal] load .dockerignore                                                                                      0.0s
     => => transferring context: 2B                                                                                        0.0s
     => [1/4] FROM ghcr.io/linuxserver/mariadb:latest@sha256:43528387250c2c65e511ef53d42a13c30fa2b03b9d1e757b6ba4aa8fc86b  0.0s
     => [internal] load build context                                                                                      0.0s
     => => transferring context: 1.09kB                                                                                    0.0s
     => CACHED [2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i  0.0s
     => [3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                          0.0s
     => [4/4] RUN date >/builddate.txt                                                                                     0.5s
     => exporting to image                                                                                                 0.1s
     => => exporting layers                                                                                                0.1s
     => => writing image sha256:773551ff31f6b2d7d4eb7fffd852e25b2eaa91b03013c9a797c2f20f3f99909f                           0.0s
    
    $ docker images
    REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
    <none>       <none>    773551ff31f6   21 seconds ago   354MB
    <none>       <none>    be00015b417c   2 minutes ago    354MB
    
    $ docker image inspect 773551ff31f6 -f '{{.ID}}'
    sha256:773551ff31f6b2d7d4eb7fffd852e25b2eaa91b03013c9a797c2f20f3f99909f
    
    $ docker ps
    CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
    $ 
  6. Paraphraser commented on Apr 27, 2025

    @Paraphraser
    Author

    @ndeloof

    My request to run docker buildx bake ... is to double check compose configures bake correctly as you run COMPOSE_BAKE=true docker compose build. A surprising point is that you get a successful build, which seems to demonstrates changes have been introduced in the image (COPY iotstack_healthcheck.sh is not tagged as CACHED) still docker compose up doesn't detect image has been updated.

    I've been reflecting on this but I am unable to convince myself that we're actually talking about the same thing. Please forgive me if I have misunderstood and wind up repeating the same material as earlier.

    Here's what I see as the problematic command sequence:

    $ export COMPOSE_BAKE=true
    $ docker compose up --build -d gitea_db

    That's the "up" command with the --build flag. That doesn't work. More specifically:

    1. It does detect the change to the health-check script; and
    2. It does generate a new image; but
    3. It does not instantiate that newly-generated image.

    Or, more simply, compose is ignoring the fact that it is an "up" command rather than a "build" command.

    Conversely, this sequence always works reliably:

    $ export COMPOSE_BAKE=true
    $ docker compose build gitea_db
    $ docker compose up -d gitea_db

    Baseline:

    $ echo "COMPOSE_BAKE=$COMPOSE_BAKE"
    COMPOSE_BAKE=true
    
    $ HEALTH_SCRIPT=./.templates/mariadb/iotstack_healthcheck.sh
    
    $ grep "^FORCE_CHANGE" $HEALTH_SCRIPT
    FORCE_CHANGE=jack
    
    $ docker ps
    CONTAINER ID   IMAGE               COMMAND   CREATED              STATUS                    PORTS     NAMES
    a041f9ca6b1e   iotstack-gitea_db   "/init"   About a minute ago   Up 56 seconds (healthy)             gitea_db
    
    $ docker exec gitea_db iotstack_healthcheck.sh
    Forced change marker = jack

    Interpretation:

    1. COMPOSE_BAKE is set.
    2. Define convenience variable.
    3. The marker in the input health-check script is "jack".
    4. The container is running.
    5. Running the health-check script inside the container returns "jack", which matches the current input script.

    Now, change the health-check script and use the "build" command (as distinct from the "up" command with the --build flag):

    $ sed -i.bak -r 's/^(FORCE_CHANGE=).*/\1bill/' $HEALTH_SCRIPT
    
    $ grep "^FORCE_CHANGE" $HEALTH_SCRIPT
    FORCE_CHANGE=bill
    
    $ docker compose build gitea_db
    [+] Building 1.8s (11/11) FINISHED                                                                                                                              
     => [internal] load local bake definitions                                                                                                                 0.0s
     => => reading from stdin 382B                                                                                                                             0.0s
     => [internal] load build definition from Dockerfile                                                                                                       0.0s
     => => transferring dockerfile: 1.16kB                                                                                                                     0.0s
     => [internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                                                        0.7s
     => [internal] load .dockerignore                                                                                                                          0.0s
     => => transferring context: 2B                                                                                                                            0.0s
     => [1/4] FROM ghcr.io/linuxserver/mariadb:latest@sha256:43528387250c2c65e511ef53d42a13c30fa2b03b9d1e757b6ba4aa8fc86b050c                                  0.0s
     => [internal] load build context                                                                                                                          0.0s
     => => transferring context: 1.08kB                                                                                                                        0.0s
     => CACHED [2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak         -e "s/^thread_cache_siz  0.0s
     => [3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                                                              0.1s
     => [4/4] RUN date >/builddate.txt                                                                                                                         0.5s
     => exporting to image                                                                                                                                     0.1s
     => => exporting layers                                                                                                                                    0.1s
     => => writing image sha256:0a936ac4d372ec501b953e85e932c50f4e326ac2f3ceee781b22b00fddd0c0f4                                                               0.0s
     => => naming to docker.io/library/iotstack-gitea_db                                                                                                       0.0s
     => resolving provenance for metadata file                                                                                                                 0.0s
    [+] Building 1/1
     ✔ gitea_db  Built                                                                                                                                         0.0s 
    
    $ docker compose up -d gitea_db
    [+] Running 1/1
     ✔ Container gitea_db  Started                                                                                                                             4.0s 
    
    $ docker ps
    CONTAINER ID   IMAGE               COMMAND   CREATED          STATUS                   PORTS     NAMES
    ff2e75564b2c   iotstack-gitea_db   "/init"   14 seconds ago   Up 9 seconds (healthy)             gitea_db
    
    $ docker exec gitea_db iotstack_healthcheck.sh
    Forced change marker = bill

    Interpretation:

    1. Edit the health-check script to change the marker to "bill".
    2. Verify the change.
    3. An explicit build detects the change (not cached).
    4. An explicit up starts the newly-built image.
    5. Different container ID confirms.
    6. Running health-check script inside container returns "bill" so we have proof the container has the updated script.

    It's expected (at least by me) that a "build" command (step 3) will always need a subsequent "up" command (step 4).


    Change the health-check script again and this time use the "problematic" up --build command:

    $ sed -i.bak -r 's/^(FORCE_CHANGE=).*/\1fred/' $HEALTH_SCRIPT
    
    $ grep "^FORCE_CHANGE" $HEALTH_SCRIPT
    FORCE_CHANGE=fred
    
    $ docker compose up -d --build gitea_db
    [+] Building 1.8s (11/11) FINISHED                                                                                                                              
     => [internal] load local bake definitions                                                                                                                 0.0s
     => => reading from stdin 382B                                                                                                                             0.0s
     => [internal] load build definition from Dockerfile                                                                                                       0.0s
     => => transferring dockerfile: 1.16kB                                                                                                                     0.0s
     => [internal] load metadata for ghcr.io/linuxserver/mariadb:latest                                                                                        0.7s
     => [internal] load .dockerignore                                                                                                                          0.0s
     => => transferring context: 2B                                                                                                                            0.0s
     => [1/4] FROM ghcr.io/linuxserver/mariadb:latest@sha256:43528387250c2c65e511ef53d42a13c30fa2b03b9d1e757b6ba4aa8fc86b050c                                  0.0s
     => [internal] load build context                                                                                                                          0.0s
     => => transferring context: 1.08kB                                                                                                                        0.0s
     => CACHED [2/4] RUN for CNF in /defaults/my.cnf /defaults/custom.cnf ; do [ -f ${CNF} ] && break ; done ;     sed -i.bak         -e "s/^thread_cache_siz  0.0s
     => [3/4] COPY iotstack_healthcheck.sh /usr/local/bin/iotstack_healthcheck.sh                                                                              0.1s
     => [4/4] RUN date >/builddate.txt                                                                                                                         0.5s
     => exporting to image                                                                                                                                     0.1s
     => => exporting layers                                                                                                                                    0.1s
     => => writing image sha256:958107fe7c892ae06bb8e0be84cf14d0e633a5391658d2f47f69f88001a54b26                                                               0.0s
     => => naming to docker.io/library/iotstack-gitea_db                                                                                                       0.0s
     => resolving provenance for metadata file                                                                                                                 0.0s
    [+] Running 2/2
     ✔ gitea_db            Built                                                                                                                               0.0s 
     ✔ Container gitea_db  Running                                                                                                                             0.0s 
    
    $ docker ps
    CONTAINER ID   IMAGE          COMMAND   CREATED          STATUS                    PORTS     NAMES
    ff2e75564b2c   0a936ac4d372   "/init"   58 seconds ago   Up 54 seconds (healthy)             gitea_db
    
    $ docker compose up -d gitea_db
    [+] Running 1/1
     ✔ Container gitea_db  Started                                                                                                                             4.0s 
    
    $ docker ps
    CONTAINER ID   IMAGE               COMMAND   CREATED          STATUS                    PORTS     NAMES
    5460a0fce489   iotstack-gitea_db   "/init"   19 seconds ago   Up 15 seconds (healthy)             gitea_db
    
    $ docker exec gitea_db iotstack_healthcheck.sh
    Forced change marker = fred

    Interpretation:

    1. Edit the health-check script to change the marker to "fred".
    2. Verify the change.
    3. An up -d --build detects the change (not cached).
    4. The old "Container ID" (ff2e75564b2c) is still running but the "IMAGE" column has changed from iotstack-gitea_db (tagged) to 0a936ac4d372 (untagged).
    5. An explicit up starts the newly-built image. The "Container ID" changes to 5460a0fce489 and the "IMAGE" column returns to the tagged name.
    6. Running health-check script inside container returns "fred" so we have proof the container has the updated script.

    It's not expected (at least not by me) that an up -d --build (step 3) will need another explicit up (step 5).


    And, for the record:

    $ docker images
    REPOSITORY               TAG       IMAGE ID       CREATED         SIZE
    iotstack-gitea_db        latest    958107fe7c89   4 minutes ago   354MB
    <none>                   <none>    0a936ac4d372   5 minutes ago   354MB
    <none>                   <none>    cdac1ccd5e66   7 minutes ago   354MB
    
  7. ndeloof commented on Apr 28, 2025

    @ndeloof
    Contributor

    It's not expected (at least not by me) that an up -d --build (step 3) will need another explicit up (step 5).

    It's not.

    I've found a possible cause for this issue: if your service uses an explicit image attribute for service image to be built, looks like Compose incorrectly collects the built digest and as such won't detect image has been updated and recreate container accordingly. Proposed a fix as #12784

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions