Search code examples
dockerdocker-compose

Conditionally start service with docker-compose


I'm looking for a way to conditionally start a service with docker-compose. Here is my scenario:

version: "3"
services:
  postgres:
    container_name: ecoo-postgres
    image: postgis/postgis
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: ecoo_db
    ports:
      - "5432:5432"

  smtp4dev:
    container_name: ecoo-smtp
    image: rnwood/smtp4dev:v3
    ports:
      - "3010:80"
      - "2525:25"

  server:
    container_name: ecoo-server
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./src:/app/src
      - ./test:/app/test
      - ./prisma:/app/prisma
    depends_on:
      - postgres
      - smtp4dev
    network_mode: "host"
    restart: always

I'm setting up the environment in my .env file, and want the smpt4dev service to conditionally start based on the ENV variable. Example: If env="dev", start the smpt4dev service, otherwise no.

Any ideas are appreciated.


Solution

  • There is no way to conditionally start a service based on an environment variable. You can conditionally start certain services when you run docker compose up by making use of docker compose profiles. For example, if you were to to use a compose file like this:

    version: "3"
    services:
      postgres:
        image: docker.io/postgis/postgis
        environment:
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
          POSTGRES_DB: ${POSTGRES_DB}
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
          interval: 10s
          timeout: 20s
          retries: 3
    
      smtp4dev:
        image: docker.io/rnwood/smtp4dev:v3
        profiles:
        - dev
    
      server:
        image: docker.io/alpinelinux/darkhttpd
        environment:
          POSTGRES_USER: ${POSTGRES_USER}
          POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
          POSTGRES_DB: ${POSTGRES_DB}
        depends_on:
          postgres:
            condition: service_healthy
    

    Then running docker compose up would start only postgres and your server, but running docker compose --profile dev up would also start the smtp4dev container.

    Alternately, you could arrange for your service to exit when the variable is unset. Something like:

      smtp4dev:
        image: docker.io/rnwood/smtp4dev:v3
        environment:
          ENV: ${ENV}
        entrypoint:
          - sh
          - -c
          - |
            [ "$ENV" = "DEV" ] || exit 0
            exec "$@"
    

    When you run docker compose up, the smtp4dev container would exit without an error unless ENV was equal to dev.

    In either of the above cases, you would need to remove the dependency between your server container and the smtp4dev container, since in many cases the smtp4dev container will not be running. Your application should gracefully handle an unavailable smtp server.

    If you feel that you really need the dependency, you could create two versions of the server container using different profiles, and only have the dependency when using --profile dev.


    Unrelated to your question, but your existing depends_on directive is effectively a no-op. To create the sort of dependencies you want, you need to (a) implement healthchecks for the target service, and then (b) use the extended form of depends_on in the dependent service.