Two project reusing the same image

Hi.
I’ve the same problem described in issue #3966: I run several projects with Docker and Docker Compose, they’re in different directory but shares the latest one or two directory names (i.e. prj1/dev/docker and prj2/dev/docker). As the issue says, I added this in my .env:

# Application configurations
APP_NAME=myprojectdev
COMPOSE_PROJECT_NAME=$APP_NAME

despite of this, when I copy the docker-compose.yaml file to another project, update .env with new APP_NAME, Docker will run the image from myprojectdev.

This is an excerpt of the compose file:

version: "3.6"

networks:
  docker-network:
    name: ${APP_NAME}_net
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: "172.30.252.0/24"

services:
  app-php: &baseapp
    container_name: ${APP_NAME}_app
    image: "my.repo.com/app:${APP_VERSION}"
    build:
      context: ../../
      target: app
    restart: unless-stopped
    expose:
      - "9000"
    networks:
      docker-network:
    environment:
      - YII_ENV=${APP_ENVIRONMENT:-production}
      - YII_DEBUG=${APP_DEBUG:-false}

I tried adding name: "customname" to the top of docker-compose.yaml but it seems behaving the same.

What did I get wrong?
Thanks

I am not sure if I would conside this as the same problem, as issue #3966 didn’t specify a project name, thus as a fallback the project name was set to the folder name app. As a result the compose files in both folders were identified as same project.

You on the other hand declare a project name by declaring COMPOSE_PROJECT_NAME in the .env file of each of those folders. The indirect assignment doesn’t change it.

Your image can only differ in the tag, so if both .env files include the same APP_VERSION value (it is missing in your snippet from above), of course both project deployments will refer to the same image. Shouldn’t your image be "my.repo.com/${APP_NAME}_app:${APP_VERSION}?

Or are you saying is that both projects deploy using the same project name? This only can happen if APP_NAME is identical in both .env files.

Please run docker compose config and share the output for both compose projects.

Thanks for the response. Pasting below the two configs.

Project 1:

name: project1dev
services:
  adminer:
    container_name: project1dev_adminer
    environment:
      ADMINER_DEFAULT_SERVER: project1dev_db
    expose:
    - "8080"
    image: adminer:4
    networks:
      docker-network: null
    restart: unless-stopped
  app-database:
    container_name: project1dev_db
    environment:
      MYSQL_DATABASE: project1_dev_db
      MYSQL_PASSWORD: passwordUoF
      MYSQL_PORT: "3306"
      MYSQL_ROOT_PASSWORD: password1
      MYSQL_USER: project1_app_user
    expose:
    - "3306"
    image: mariadb:10.6
    networks:
      docker-network: null
    ports:
    - mode: ingress
      target: 3306
      published: "3316"
      protocol: tcp
    restart: unless-stopped
    volumes:
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/mysql/data
      target: /var/lib/mysql
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/mysql/conf/yetopen.cnf
      target: /etc/mysql/conf.d/yetopen.cnf
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/mysql/files
      target: /var/lib/mysql-files
      bind:
        create_host_path: true
  app-php:
    build:
      context: /Users/maxxer/work/project1servizi-gestionale
      dockerfile: Dockerfile
      target: app
    container_name: project1dev_app
    depends_on:
      app-database:
        condition: service_started
    environment:
      APP_SKIP_MIGRATION: "0"
      DB_HOST: project1dev_db
      DB_NAME: project1_dev_db
      DB_PASSWORD: passwordUoF
      DB_PORT: "3306"
      DB_USER: project1_app_user
      PHP_GROUP_ID: docker
      PHP_USER_ID: docker
      YII_DEBUG: "false"
      YII_ENV: production
    expose:
    - "9000"
    image: img.domain.com/project1-servizi/gestionale-project1-servizi/app:main
    networks:
      docker-network: null
    restart: unless-stopped
    volumes:
    - type: volume
      source: webapp-data
      target: /dati/www
      volume: {}
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/config/db.php
      target: /dati/www/config/db.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/config/params.php
      target: /dati/www/config/params.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/app-files/uploads
      target: /dati/www/uploads
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/app-files/runtime
      target: /dati/www/runtime
      bind:
        create_host_path: true
  app-webserver:
    container_name: project1dev_webserver
    depends_on:
      app-php:
        condition: service_started
    environment:
      ADMINER_RESTRICTIONS: 172.30.252.0\/24
      ADMINER_SUFFIX: yu43YpdkHmU
      NGINX_FASTCGI_CONNECT_TIMEOUT: 60s
      NGINX_FASTCGI_READ_TIMEOUT: 60s
      NGINX_MAX_BODY_SIZE: 100m
      NGINX_PROXY_CONNECT_TIMEOUT: 60s
      NGINX_PROXY_READ_TIMEOUT: 60s
      PHP_FPM_HOST: project1dev_app
    image: nginx:1.23
    networks:
      docker-network: null
    ports:
    - mode: ingress
      target: 8000
      published: "3380"
      protocol: tcp
    - mode: ingress
      target: 8001
      published: "3381"
      protocol: tcp
    restart: unless-stopped
    volumes:
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/nginx/templates
      target: /etc/nginx/templates
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/nginx/entrypoint/90-adminer-restrictions.sh
      target: /docker-entrypoint.d/90-adminer-restrictions.sh
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/nginx/entrypoint/91-generate-ssl-cert.sh
      target: /docker-entrypoint.d/91-generate-ssl-cert.sh
      read_only: true
      bind:
        create_host_path: true
    - type: volume
      source: webapp-data
      target: /dati/www
      volume: {}
  init:
    build:
      context: /Users/maxxer/work/project1servizi-gestionale
      dockerfile: Dockerfile
      target: app
    command:
    - /usr/local/bin/app_init_entrypoint.sh
    container_name: project1dev_init
    depends_on:
      app-database:
        condition: service_started
    environment:
      APP_SKIP_MIGRATION: "0"
      DB_HOST: project1dev_db
      DB_NAME: project1_dev_db
      DB_PASSWORD: passwordUoF
      DB_PORT: "3306"
      DB_USER: project1_app_user
      PHP_GROUP_ID: docker
      PHP_USER_ID: docker
      YII_DEBUG: "false"
      YII_ENV: production
    expose:
    - "9000"
    image: img.domain.com/project1-servizi/gestionale-project1-servizi/app:main
    networks:
      docker-network: null
    restart: "no"
    volumes:
    - type: volume
      source: webapp-data
      target: /dati/www
      volume: {}
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/config/db.php
      target: /dati/www/config/db.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/config/params.php
      target: /dati/www/config/params.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/app-files/uploads
      target: /dati/www/uploads
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/app-files/runtime
      target: /dati/www/runtime
      bind:
        create_host_path: true
  workers:
    build:
      context: /Users/maxxer/work/project1servizi-gestionale
      dockerfile: Dockerfile
      target: worker
    container_name: project1dev_worker
    depends_on:
      init:
        condition: service_started
    environment:
      DB_HOST: project1dev_db
      DB_NAME: project1_dev_db
      DB_PASSWORD: passwordUoF
      DB_PORT: "3306"
      DB_USER: project1_app_user
    image: img.domain.com/project1-servizi/gestionale-project1-servizi/worker:main
    networks:
      docker-network: null
    volumes:
    - type: volume
      source: webapp-data
      target: /dati/www
      volume: {}
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/worker/cron.d
      target: /etc/cron.d
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/worker/supervisord.conf
      target: /etc/supervisord.conf
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project1servizi-gestionale/dev/docker/worker/supervisord-includes
      target: /yetopen/supervisord
      read_only: true
      bind:
        create_host_path: true
networks:
  docker-network:
    name: project1dev_net
    driver: bridge
    ipam:
      driver: default
      config:
      - subnet: 172.30.252.0/24
volumes:
  webapp-data:
    name: project1dev-webapp-data

Project 2:

name: project2dev
services:
  adminer:
    container_name: project2dev_adminer
    environment:
      ADMINER_DEFAULT_SERVER: project2dev_db
    expose:
    - "8080"
    image: adminer:4
    networks:
      docker-network: null
    restart: unless-stopped
  app-database:
    container_name: project2dev_db
    environment:
      MYSQL_DATABASE: project2_dev_db
      MYSQL_PASSWORD: password2
      MYSQL_PORT: "3306"
      MYSQL_ROOT_PASSWORD: password2
      MYSQL_USER: project2_app_user
    expose:
    - "3306"
    image: mariadb:10.3
    networks:
      docker-network: null
    ports:
    - mode: ingress
      target: 3306
      published: "3326"
      protocol: tcp
    restart: unless-stopped
    volumes:
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/mysql/data
      target: /var/lib/mysql
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/mysql/conf/yetopen.cnf
      target: /etc/mysql/conf.d/yetopen.cnf
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/mysql/files
      target: /var/lib/mysql-files
      bind:
        create_host_path: true
  app-php:
    build:
      context: /Users/maxxer/work/project2
      dockerfile: Dockerfile
      target: app
    container_name: project2dev_app
    depends_on:
      app-database:
        condition: service_started
    environment:
      APP_SKIP_MIGRATION: "0"
      DB_HOST: project2dev_db
      DB_NAME: project2_dev_db
      DB_PASSWORD: password2
      DB_PORT: "3306"
      DB_USER: project2_app_user
      PHP_GROUP_ID: docker
      PHP_USER_ID: docker
      YII_DEBUG: "false"
      YII_ENV: production
    expose:
    - "9000"
    image: img.domain.com/yetopen/project2/app:master
    networks:
      docker-network: null
    restart: unless-stopped
    volumes:
    - type: volume
      source: webapp-data
      target: /dati/www
      volume: {}
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/config/db.php
      target: /dati/www/config/db.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/config/params.php
      target: /dati/www/config/params.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/config/ldap_config.php
      target: /dati/www/config/ldap_config.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/config/settings.php
      target: /dati/www/config/settings.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/config/yii_env_debug.php
      target: /dati/www/config/yii_env_debug.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/app-files/uploads
      target: /dati/www/uploads
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/app-files/runtime
      target: /dati/www/runtime
      bind:
        create_host_path: true
  app-webserver:
    container_name: project2dev_webserver
    depends_on:
      app-php:
        condition: service_started
    environment:
      ADMINER_RESTRICTIONS: 172.30.251.0/24
      ADMINER_SUFFIX: uBv5zvbpTUTRhTCVnh
      NGINX_FASTCGI_CONNECT_TIMEOUT: 60s
      NGINX_FASTCGI_READ_TIMEOUT: 60s
      NGINX_MAX_BODY_SIZE: 100m
      NGINX_PROXY_CONNECT_TIMEOUT: 60s
      NGINX_PROXY_READ_TIMEOUT: 60s
      PHP_FPM_HOST: project2dev_app
    image: nginx:1.23
    networks:
      docker-network: null
    ports:
    - mode: ingress
      target: 8000
      published: "3382"
      protocol: tcp
    restart: unless-stopped
    volumes:
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/nginx/templates
      target: /etc/nginx/templates
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/nginx/entrypoint/90-adminer-restrictions.sh
      target: /docker-entrypoint.d/90-adminer-restrictions.sh
      read_only: true
      bind:
        create_host_path: true
    - type: volume
      source: webapp-data
      target: /dati/www
      volume: {}
  init:
    build:
      context: /Users/maxxer/work/project2
      dockerfile: Dockerfile
      target: app
    command:
    - /usr/local/bin/app_init_entrypoint.sh
    container_name: project2dev_init
    depends_on:
      app-database:
        condition: service_started
    environment:
      APP_SKIP_MIGRATION: "0"
      DB_HOST: project2dev_db
      DB_NAME: project2_dev_db
      DB_PASSWORD: password2
      DB_PORT: "3306"
      DB_USER: project2_app_user
      PHP_GROUP_ID: docker
      PHP_USER_ID: docker
      YII_DEBUG: "false"
      YII_ENV: production
    expose:
    - "9000"
    image: img.domain.com/yetopen/project2/app:master
    networks:
      docker-network: null
    restart: "no"
    volumes:
    - type: volume
      source: webapp-data
      target: /dati/www
      volume: {}
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/config/db.php
      target: /dati/www/config/db.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/config/params.php
      target: /dati/www/config/params.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/config/ldap_config.php
      target: /dati/www/config/ldap_config.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/config/settings.php
      target: /dati/www/config/settings.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/config/yii_env_debug.php
      target: /dati/www/config/yii_env_debug.php
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/app-files/uploads
      target: /dati/www/uploads
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/app-files/runtime
      target: /dati/www/runtime
      bind:
        create_host_path: true
  workers:
    build:
      context: /Users/maxxer/work/project2
      dockerfile: Dockerfile
      target: worker
    container_name: project2dev_worker
    depends_on:
      init:
        condition: service_started
    environment:
      DB_HOST: project2dev_db
      DB_NAME: project2_dev_db
      DB_PASSWORD: password2
      DB_PORT: "3306"
      DB_USER: project2_app_user
    image: img.domain.com/yetopen/project2/worker:master
    networks:
      docker-network: null
    volumes:
    - type: volume
      source: webapp-data
      target: /dati/www
      volume: {}
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/worker/cron.d
      target: /etc/cron.d
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/worker/supervisord.conf
      target: /etc/supervisord.conf
      read_only: true
      bind:
        create_host_path: true
    - type: bind
      source: /Users/maxxer/work/project2/dev/docker/worker/supervisord-includes
      target: /yetopen/supervisord
      read_only: true
      bind:
        create_host_path: true
networks:
  docker-network:
    name: project2dev_net
    driver: bridge
    ipam:
      driver: default
      config:
      - subnet: 172.30.251.0/24
volumes:
  webapp-data:
    name: project2dev-webapp-data

I have never seen the top level element “name”.

Apart from that, both rendered compose files appear distinct. I assume you declared the COMPOSE_PROJECT_NAME in the .env file, so that the object labels belong to the project name.

I don’t see any reason why the same image should be used, especially since both compose files use different group and repo names. Though, the build cache will probably re-use existing layers from the other project. If the Dockerfiles are identical the build would pick all unchanged layers from the cache.

In case you want to force builds without cache you can define:

services
  myservice:
    build:
      no_cache: true
      ...
   ...
...

You can always use docker inspect and check the labels of an object to see to which project it belongs.

I might have found the issue, which as might happen, lies in small bits I didn’t consider sharing :man_facepalming:.

I have an override file for development purposes, and in this file (pasted below), the image param was the same for both projects. This made compose use the very same image.

version: "3.6"

services:
  app-webserver:
    volumes:
      - ../../:/dati/www
    environment:
      # Have nginx wait for php-fpm when debugging
      - NGINX_FASTCGI_READ_TIMEOUT=300s
  app-php:
    image: "app-debug"
    build:
      target: app-debug
      args:
        - RUN_COMPOSER="false"
    environment:
      - YII_ENV=${APP_ENVIRONMENT:-dev}
      - YII_DEBUG=${APP_DEBUG:-true}
    volumes:
      - ../../:/dati/www
      - ~/work:/dati/work
      # Those must be "rewritten, as `volumes` are merged by mountpoint
      - ../../config/params.php:/dati/www/config/params.php:ro
      # Sample useful mount points
      - ~/.composer:/var/www/.composer
      - ~/.ssh:/var/www/.ssh
  workers:
    volumes:
      - ../../:/dati/www
  init:
    volumes:
      - ../../:/dati/www
      # Those must be "rewritten, as `volumes` are merged by mountpoint
      - ../../config/params.php:/dati/www/config/params.php:ro

updating it with

  app-php:
    image: "app-debug-${APP_NAME}"

worked!

@meyay thank you for your time and efforts!