The absolute best way to manage complex startup commands like this is to (a) write them as shell scripts and (b) bake them into your Docker image.
For instance, I can reinterpret the startup procedure that you wrote as pseudocode like:
1. cd into the wait-for-it directory and run the wait-for-it script
2. Do the thing the container was originally going to do
I can translate that into a shell script:
#!/bin/sh
(cd /app/wait-for-it && ./wait-for-it.sh "$DB_HOST_PORT")
exec "$@"
Then I can use that script as an ENTRYPOINT
in my image. It runs the cd
command in a subshell so it returns back to the original directory on the next line; the exec "$@"
line runs what was passed as a command. The end of your Dockerfile
would look like:
COPY entrypoint.sh /app
RUN chmod +x entrypoint.sh
WORKDIR /app/api
ENTRYPOINT ["/app/entrypoint.sh"]
CMD ["yarn", "start"]
Then I don't need to manually override these settings in the docker-compose.yml
at all; the only thing I need to do is to provide the database location as an environment variable.
version: '3'
services:
myapp:
build: .
environment:
- DB_HOST_PORT=postgres:5432
ports:
- '3000:3000'
postgres:
image: postgres:12
If I wanted to run some other command:
docker-compose run myapp yarn migrate
It would still wait for the database to be available, but then run the alternate command.