Accessing the priv folder from Mix release

I’m deploying a Phoenix app with Mix releases. I’d like to access a static file, stored in the priv/ directory, during runtime.

When I enter the running app, I can see the following directory structure:

iex(backend@5714fba14b09)10> File.ls()                          
{:ok, ["lib", "tmp", "bin", "erts-10.4.4", "releases", "Procfile"]}

If I want to access anything in priv, I have to use the full path lib/backend-2.2.0/priv/.../myfile.json (for example). I could write code to generate this path based on the version and app name but that seems fragile – is there a better way? I tried looking into how Ecto does this for migrations, but with no luck!

2 Likes

The most idiomatic and most reliable and only correct way is to use :code.priv_dir/1.

7 Likes

Example of use in an attribute (compile time resolution) - also works in a release (haven’t tested moving the release to another dir (I’m working with docker (alpine-elixir)) - if you’re going to do that - you’ll probably want to make it a function instead.

In elixir I feel Application.app_dir/2 is more ideomatic.

2 Likes

No, compile time resolution will only work by accident, please resolve at runtime, it’s fast enough.

I checked your code, and in theory it will only ever work when you run the release from a machine that still has the source folder available at it’s original location.

2 Likes

Oh, since when is that there? I should try to remember that one.


Edit

I read the docs, it does not necessarily point to something that contains the priv folder… It points to where the compiled modules will be… Or I’m missinterpreting the docs… I’d stick to :code.priv_dir.

3 Likes

If you do Application.app_dir(:myapp, "priv/static/file.ext") it’ll work. It surely isn’t the same as :code.priv_dir as it’s always starting at the parent of the priv dir, but it works. E.g. Plug.Static is using it.

5 Likes

Thank you both!

I’ve noticed that neither Application.app_dir/2 not :code.priv_dir/1 seem to work in config files, since the app does not exist yet. Is there any way around this?

5 Likes

Configure only the path and feed it to those functions as runtime.

3 Likes

How to do this?

# in config
config :my_app, path: "…"

# in your code
rel = Application.fetch_env!(:my_app, :path)
path = Application.app_dir(:my_app, path)
3 Likes