1

I have this docker-compose.yml which runs a node script which depends on Redis.

version: "3.9"
services:
  redis:
    image: "redis:alpine"
    # restart: always
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - ./docker/redis:/data
  node:
    image: "node:17-alpine"
    user: "node"
    depends_on:
      - redis
    environment:
      - NODE_ENV=production
      - REDIS_HOST_ENV=redis
    volumes:
      - ./docker/node/src:/home/node/app
      - ./docker/node/log:/home/node/log
    expose:
      - "8081"
    working_dir: /home/node/app
    command: "npm start"

When starting this script with docker compose up both services will start. However when the node service is finished, the redis service keeps running. Is there a way to define that the redis service can stop when the node service is done?

Bas van Dijk
  • 9,933
  • 10
  • 55
  • 91
  • 1
    [How to stop all containers when one container stops with docker-compose?](https://stackoverflow.com/questions/33799885/how-to-stop-all-containers-when-one-container-stops-with-docker-compose) suggests `docker-compose up --abort-on-container-exit`, though that requires you to run the containers in the foreground and might not scale to larger applications. Is that enough for your needs? – David Maze Nov 07 '21 at 21:32
  • That sounds like what I am looking for! Thanks! – Bas van Dijk Nov 14 '21 at 15:29

1 Answers1

0

I have examined the documentation for Compose Spec but I have not found anything that allows you to immediately stop containers based on the state of another one. Perhaps there really is a way, but you can always control the behaviour of the redis service by using an healthcheck:

services:
  redis:
    image: "redis:alpine"
    # restart: always
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - ./docker/redis:/data
    healthcheck:
      test: ping -c 2 mynode || kill 1
      interval: 5s
      retries: 1
      start_period: 20s
  node:
    image: "node:17-alpine"
    container_name: mynode
    user: "node"
    depends_on:
      - redis
    environment:
      - NODE_ENV=production
      - REDIS_HOST_ENV=redis
    volumes:
      - ./docker/node/src:/home/node/app
      - ./docker/node/log:/home/node/log
    expose:
      - "8081"
    working_dir: /home/node/app
    command: "npm start"

As for the node service, I have added a container_name: mynode, necessary by the redis service in order to contact it. The container name becomes also the hostname, if not specified with the hostname property.

The redis service has an healthcheck that ping the node container every 5 seconds, starting after 30 seconds from the container start. If the ping is successful, the container is labeled as healthy, otherwise it is killed.

This solution might work in your case but has some downsides:

  • The healthcheck feature is abused here, besides what if you had another healthcheck?
  • You cannot always kill the init process, because protected by default. There are some discussions about this and it seems the most popular decision is to use tini as the init process. Fortunately, in the image you are using, it is possible.
  • redis service contacts the node service via the hostname, which means that they are supposed to be in the same network in your case. The current network is the default bridge network that should be avoided most of the times. I suggest you to declare a custom bridge network.
  • This solution is based on polling the node container, which is not very elegant, firstly because you have to hope that the time-based parameters in the healthcheck section are "good-enough".
Marco Luzzara
  • 5,540
  • 3
  • 16
  • 42
  • 1
    Thanks for your detailed answer! Using healthcheck for this purpose feels a bit odd. The command `docker-compose up --abort-on-container-exit` suggested in the comments looks like a better fitting solution – Bas van Dijk Nov 14 '21 at 15:29
  • it is definitely odd but it is one of the only option you have as far as I know. `--abort-on-container-exit` does not let you to control the behaviour of every single service, but I am glad it is enough for you :) – Marco Luzzara Nov 14 '21 at 15:42