Database migrations are used to automatically keep the database in sync with the code that uses it. Elixir apps should be deployed as releases, supervised by systemd. Here is an example of how to run migrations when deploying Elixir releases.
It's tempting to automatically run database migrations when the app starts up. Once we get into more complex deployment scenarios, however, that can cause more problems than it solves. It's better to run migrations separately.
For production systems, it's important to be able to roll back code in case of problems. It's a lot easier to roll back code than data, though.
Whenever possible we make all our database changes work with old and new code releases. For example, we first run a db change which adds a column to a db, make sure that it's working properly with the old code, then deploy new code which uses the new column. If we need to roll back, then we know that the old code will still work with the new db schema.
We may also be running code in AWS in an Auto Scaling Group, deploying via a Blue/Green process with AWS CodeDeploy. This means that the db might be shared between old and new code at runtime as we upgrade the cluster.
Our normal process is to run the db migrations from a "Operations and Maintenance" instance in the deploy environment, e.g. the build server. In the test environment, we check out the code and run migrations against the db. We verify that it's working properly, then we deploy the new code and verify it works. Then we run the db updates in the production environment and deploy the code.