We are trying to deploy a Phoenix app to Fly.io that requires some data to be initialised in the PostgreSQL DB.
We followed the Elixir getting started guide: Deploy an Elixir Phoenix Application and have done a bunch of googling/debugging and come to the conclusion that the /priv/repo/seeds.exs file is not available during the mix release …
So my/our question is: has anyone else faced this issue and how can we seed data into a freshly deployed Phoenix App? (or is there a generic approach to seeding data in any programming language/framework that we can adapt…?)
Yes, the seeds aren’t auto-run. That’s a general situation with Elixir releases. Typically, you only want them run once. I like to write my seeds so they can be run multiple times (ie idempotent). However, it’s unique to each project and that means the solution is as well.
What I do is put the seeds into a regular module like MyApp.GlobalSetup. Then define a function like seed/0 or populate_pick_list_options or whatever.
Then SSH into an instance and run it in an IEx shell. Ex: MyApp.GlobalSetup.seed.
@Mark just to clarify, write the seed logic into a module, so not using seeds.exs or similar at all, i.e. don’t run the existing seed script from the new module?
Indeed. What worked for us was following @Mark’s advice and shifting the database initialisation code out of seeds.exs to a new file lib/init.ex and then invoking it from priv/repo/seeds.exs on localhost.
We didn’t run the seeds.exs on Fly.io rather we created an /init endpoint+controller that would run the Init.main/0 function idempotently. It’s probably more steps than a typical app would require but it meant we could have a nice initialisation (“status”) page for our app:
I come from React and Front End land and am a Elixir, Phoenix, Fly noob here. How exactly would I go about doing this? My assumption is that in the MyApp.GlobalSetup I would need to execute some kind of Mix.Task and like everything else I am doing right now writing a custom mix task is a first I am also assuming that the seeds.exs is copied to the fly server instance since the entire priv directory is copied in the Dockerfile
What I have stubbed out at the moment (that is making iEX scream):
defmodule GlobalSetup do
@moduledoc """
Module for any global setup that needs to be executed
on Fly.io that I cannot just have done in the Dockerfile.
"""
use Mix.Task
@impl Mix.Task
def run(_args) do
Mix.shell().info("Seeding the database via seeds.exs...")
Mix.shell().run("priv/repo/seeds.exs")
end
end
Any pointers would be greatly appreciated! Btw love reading your content for Fly, I come across it regularly on Twitter.
Hi @rockchalkwushock! Yes, @lubien is right, the last bit to specify is that you can run that code through fly ssh console. Once you start an IEx session on your project, then can execute the seeds. Ex: GlobalSetup.run.
It’s fine that it’s a manual process because you only do it once when setting up your project.
Yes, “Mix” is not available in an Elixir release. It is a “dev” tool only. When deploying an Elixir app to Fly, the Dockerfile (generated by Phoenix) builds a “release”. This does not support mix tasks.
Please refer to the conversation here as it describes a way to write the operations you need into your application. Then they can be run through an IEx shell.
An other approach I found, is simply running the seed file from the iEX ssh console (fly ssh console --pty -C "/app/bin/myapp remote") with this function:
Be careful to investigate first your file path by doing fly ssh console.
Also, if you want to handle “dev” and “prod” at the same time in a function, you can handle that with a if condition or pattern match according to System.get_env("MIX_ENV") == "prod".
Dev and prod paths will be different if you reference other files.