Do you ever want to run a CLI before your main app in Docker? Here is a complicated way to do just that.

We need a folder named success_flag that will contain a flag for the main app to know when the cli has finished running.

First, we delete any existing success flag that remains:

  base:
    build: .
    container_name: base
    volumes:
      - ./success_flag:/success_flag
    command: bash -c "rm -f /success_flag/cli_success"
    restart: "no"

Second, let’s run the database layer, in this case Postgres (but it can be any database engine).

Third we run the cli, mapping a volume with the success_flag folder:

  cli:
    build: 
      context: .
      dockerfile: Dockerfile.cli
    environment:
      - CONNECTION_STRING=postgresql://postgres:postgres@postgres:5432/test
    volumes:
      - ./success_flag:/success_flag  # Mount a volume for success flag
    networks:
      - backend
    depends_on:
      - postgres
    restart: "no"  # Do not restart after completion

Fourth, let’s run the main app, also with mapping a volume with the success_flag folder.

  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: app
    depends_on:
      - cli  # Ensure cli.py completes
    volumes:
      - ./success_flag:/success_flag  # Ensure the success flag is accessible
    ports:
      - "5000:5000"
    environment:
      - CONNECTION_STRING=postgresql://postgres:postgres@postgres:5432/test
    entrypoint: ["./wait-for-cli.sh"]
    command: ["gunicorn", "--bind", "0.0.0.0:5000", "wsgi:app"]  # Replace with the actual command to start your application
    networks:
      - backend
    restart: unless-stopped

Also we need to change the entrypoint of the Docker container to ./wait-for-cli.sh. The batch file waits for the success flag and then runs the startup command:

#!/bin/sh

# wait-for-cli.sh
set -e

# Wait for the cli success file to be created
while [ ! -f /success_flag/cli_success ]; do
  echo "Waiting for cli to complete..."
  sleep 2  # Wait a bit before checking again
done

echo "cli has completed successfully. Starting main app."

# Start the main app
exec "$@"

For completion, here is all the docker compose file :

services:

  base:
    build: .
    container_name: base
    volumes:
      - ./success_flag:/success_flag
    command: bash -c "rm -f /success_flag/cli_success"
    restart: "no"

  postgres:
    image: postgres
    container_name: postgres
    depends_on:
      - base
    volumes:
      - pdsql_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
    ports:
      - 5432
    restart: unless-stopped
    networks:
      - backend

  cli:
    build: 
      context: .
      dockerfile: Dockerfile.cli
    environment:
      - CONNECTION_STRING=postgresql://postgres:postgres@postgres:5432/test
    volumes:
      - ./success_flag:/success_flag  # Mount a volume for success flag
    networks:
      - backend
    depends_on:
      - postgres
    restart: "no"  # Do not restart after completion

  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: app
    depends_on:
      - cli  # Ensure cli.py completes
    volumes:
      - ./success_flag:/success_flag  # Ensure the success flag is accessible
    ports:
      - 5000:5000
    environment:
      - CONNECTION_STRING=postgresql://postgres:postgres@postgres:5432/test
    entrypoint: ["./wait-for-cli.sh"]
    command: ["gunicorn", "--bind", "0.0.0.0:5000", "wsgi:app"]  # Replace with the actual command to start your application
    networks:
      - backend
    restart: unless-stopped

volumes:
  pdsql_data:

networks:
  backend:
    driver: bridge