I am trying to set up a development environment using docker-compose where I need my web service to perform a database migration and then start the application server immediately after. I've tried listing commands under the 'command' key, but only the last one seems to run. Is there a way to chain these commands together effectively, or do I need to create a custom entrypoint script for every single service in my YAML file?
3 answers
The most common way to execute multiple commands without creating an external script is by using the sh -c (or /bin/bash -c) syntax within your docker-compose.yml. You can chain commands using the && operator, which ensures that the second command only runs if the first one succeeds. For example: command: sh -c "python manage.py migrate && python manage.py runserver 0.0.0.0:8000". This keeps your configuration contained within the YAML file. However, remember that the shell becomes PID 1, so if you want your application to receive OS signals (like SIGTERM), you should use exec for the final command in the string to replace the shell process with your app process.
If one of those commands fails in the middle of a production deployment, how does Docker Compose handle the container status? Does it restart the whole sequence from the beginning?
For complex logic involving more than two commands, I highly recommend using an entrypoint.sh script. You just copy it into the image and call it from the compose file; it’s much cleaner.
I agree with Linda. Chaining too many commands in a YAML file makes it very hard to read and debug. I usually put all my environment checks and migrations in a script, then end it with exec "$@" so that the command passed from docker-compose still runs as the main process. It's a much more robust pattern for enterprise-grade Cloud Technology projects.
Christopher, that depends entirely on your restart_policy. If a command in your && chain fails, the shell exits with an error code, causing the container to stop. If you have restart: always or on-failure enabled, Docker will indeed restart the container and attempt the entire sequence again. This is why it is vital to make your initialization commands "idempotent"—meaning they can be run multiple times without causing errors or duplicate data.