What is the best practice to run ecto migration before deploying?

I’m deploying my app with fly.io.

I have no idea how to run ecto migration before deploying.
If I don’t do that, new version app will make errors because of broken DB schema.

I only have the information about prod DB in secrets of fly.io, not in local.

1 Like

Take a look at the Phoenix’s deployment guide. I think people usually run the migrations right before the app starts. I run those as one Docker command:

CMD ["sh", "-c", "bin/app eval MyApp.Release.migrate && bin/app start"]
5 Likes

And I do the same as an ExecStartPre in systemd.

ExecStartPre= /opt/admin/bin/admin eval "Admin.Release.migrate()"
ExecStart= /opt/admin/bin/admin start
ExecStop= /opt/admin/bin/admin stop
3 Likes

How do folks handle it when deploying to multiple nodes/instances? I would want to only execute the migration script once per deployment, and not per started app instance.

For k8s - initContainer comes to mind.

Marking an instance to only run migrations there, is also an option. That’s what rails deployment with capistrano looked like back in the days.

I don’t think you need to take any special care. Ecto takes care of this case by locking the migrations table:

https://hexdocs.pm/ecto_sql/Ecto.Migrator.html#run/4-execution-model

Another reason to love Ecto :slight_smile:

6 Likes

Nice. Didn’t know about that

Highly recommend the Safe Ecto Migrations series of posts — gave me a lot more confidence and understanding in this area.

6 Likes

Wondering, what actually happens when a second instance sees a migration lock? Does it wait to start?

1 Like

Yes. The first instance to acquire the lock will run the migrations. All other instances will wait for the first instance to finish and release the lock - they will then acquire the lock in turn, see the database already migrated, and do nothing.

6 Likes

Hey everyone, I know this is an old thread but building on the accepted answer, I ran into an issue where docker compose stop was timing out on Elixir containers. The problem was the shell script (sh) handling SIGTERM as PID 1 instead of the BEAM.

To fix this, I updated the custom Phoenix server script (rel/overlays/bin/server):

#!/bin/sh
set -eu

cd -P -- "$(dirname -- "$0")"
# added this migration line
./my_app eval MyApp.Release.migrate 

PHX_SERVER=true exec ./my_app start

With this, the Dockerfile stays:

CMD ["/app/bin/server"]

Now migrations run on startup, and SIGTERM signals are properly handled. Hope this helps someone that may be going through the same problem! Thanks! (:

2 Likes