Where to put Phoenix configs within an Elixir umbrella app

Hello everybody,

I’m following a somehow old tutorial about Phoenix (Getting Started with Phoenix from Pluralsight) where ecto isn’t used within phoenix but rather in a regular elixir app under an umbrella app.

Here are the basic steps I did:

> mix new my_app_umbrella
> cd my_app_umbrella/apps
> mix phx.new my_web_app --no-ecto
> mix new my_app --sup

I’m stumbling on weird configs related errors right at the beginning (I’m using the latest version of Elixir/Phoenix/Ecto).

Both the Phoenix app and the Elixir/Ecto apps were correctly generated using mix under the umbrella app (say at ~/app_umbrella) since I’m correctly getting the following lines on both mix.exs files of the subapps:

build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",

… as it is explained here in the guides (https://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-projects.html)

So I understood that every configuration of the subapps should reside on the umbrella app config file (at as it’s explained in the top comment of the config.exs file of the umbrella app):

# This file is responsible for configuring your umbrella
# and **all applications** and their dependencies with the
# help of the Config module.

As a side note, is it fine to have every subapps configuration mixed up in the same parent/umbrella config file?

And Mix didn’t even generated a config folder in the supapp which somehow makes sense regarding what I understood so far)

However, all the config files that Phoenix generated are still located on its own config directory (eg. in ~/app_umbrella/apps/my_web_app/config.
And then I’m having some weird warnings (e.g. regarding Poison since the fact Jason is declared on the config.exs file from the local Phoenix config folder…)

So I try to move everything from ~/app_umbrella/apps/my_web_app/config/config.exs to the umbrella app in ~/app_umbrella/config/config.exs, but this doesn’t work unless I move everything (like the environment config files such as dev.exs files, etc.)

But this sounds weird to me… Am I missing something?

[ Edit 1: Also, I forgot to mention, the “import” statement isn’t even the same…
On the umbrella config.exs this is the first statement: import Config.
While on the Phoenix config.exs this is the first statement: use Mix.Config
What are the differences, which one should I keep, if I move everything from ~/app_umbrella/apps/my_web_app/config to ~/app_umbrella/config? ]

Do you know what’s the best way to have this setup (Phoenix without ecto, and elixir with ecto) under an umbrella app?

Thank you very much…

NB: Not sure if it’s more a Phoenix related stuff or an Elixir one…

[ Edit 2: When I’m trying to generate the configuration file of ecto like so:

~/app_umbrella/apps/my_app> mix ecto.gen.repo -r MyApp.Repo

I’m getting an error about config.exs not existing…
It seems that the paths defined on the mix.exs files aren’t taken into account… ]

As a side note, is it fine to have every subapps configuration mixed up in the same parent/umbrella config file?

Yes, that is the recommended way. The umbrella combines apps to one application and this application has one config.

And Mix didn’t even generated a config folder in the supapp which somehow makes sense regarding what I understood so far)

The generation of the config directory was removed explicitly some versions ago. See Do not generate config/config.exs for mix new · Issue #8815 · elixir-lang/elixir · GitHub and No longer generate config for mix new by josevalim · Pull Request #8932 · elixir-lang/elixir · GitHub

On the umbrella config.exs this is the first statement: import Config .
While on the Phoenix config.exs this is the first statement: use Mix.Config
What are the differences, which one should I keep,

import Config is the new use Mix.Config, see Config — Elixir v1.16.0
For the differences in general see https://elixir-lang.org/getting-started/alias-require-and-import.html

1 Like

To make life easier, I would install the latest Elixir and Phoenix then use mix phx.new my_app --umbrella to get an umbrella with one app including Ecto and one app including Phoenix. You can then study the generated files and see how the new combined config works differently than the old pluralsight post you were following.

Once you have this umbrella set up you can cd my_app_umbrella/apps and create more apps inside your umbrella project when needed. Empty Elixir (library apps) with mix new my_lib, supervised apps with mix new my_server --sup, extra Ecto apps with mix phx.new.ecto other_db, and extra Phoenix endpoints with mix phx.new.web admin.

I’m pretty sure you need to use Elixir 1.9+ to use import Config that’s why Phoenix 1.4.x still defaults to use Mix.Config. Since you are already on 1.9+ you can follow @Marcus link about migration after creating your umbrella.

3 Likes

Thank you for the details…
So, moving every configuration file from the nested config directory of the phoenix app to the parent umbrella app is correct?
But isn’t Mix with the config path config_path: "../../config/config.exs", should be able to manage that in the first place by putting everything there?
Also, why this path isn’t taken into account with e.g. mix ecto.gen.repo?

Thanks for the details…

I got the fact to build dedicated app under un umbrella app and it does make sense.
So at the beginning I only wanted to know if it’s fine to move all the configuration file from the phoenix directory to the top umbrella app…
I thought that the umbrella app should be a regular elixir app but so it might be anything…

So I wondered how I can translate my initial goal (trying to follow along an old tutorial) to have the following working:

> mix new my_app_umbrella
> cd my_app_umbrella/apps
> mix phx.new my_web_app --no-ecto
> mix new my_app --sup

Here I’m noticing that I’m ending up with three apps:

my_app_umbrella/apps/my_app/lib/my_app             #our custom domain logic (with ecto and supervised)
my_app_umbrella/apps/my_web_app/lib/my_web_app_web #the web part of Phoenix
my_app_umbrella/apps/my_web_app/lib/my_web_app     #the domain logic part of Phoenix (hence without ecto)

But I start thinking that something is definitely awkward here…

So I tested with mix phx.new my_app --umbrella as you suggested.

First I noticed that in this case mix appended _umbrella by itself for the root directory and that each of the lib/my_app_web and lib/my_app we used to have in Phoenix app are now under the umbrella apps directory my_app_umbrella/apps/.

Then in this case everything are in the single config file under the config directory of the umbrella app my_app/config/config.exs and I’m simply ending up with two apps:

my_app_umbrella/apps/my_app/lib/my_app         #the domain logic generated by phoenix (with ecto included)
my_app_umbrella/apps/my_app_web/lib/my_app_web #the web part of Phoenix

Indeed this seems pretty clean.

Now I’m thinking that this is what was initially intended by the author course, but I guess that in that time Elixir and Phoenix were not configured to behave like this.

But now I wonder what is the difference between a regular phoenix app and an umbrella phoenix app?
Which one is better to follow? It seems that it’s only a matter of directory structure…

Anyway thank you…

No problem at all… yes Elixir/Phoenix has evolved since the article was written so I would definitely use the official generators for a very clean starting point.

It’s actually called an Umbrella project… a grouping of Elixir apps that must share the same config and dependencies… it provides several conveniences but technically is not an app on its own.

There are a large number of posts on the forum you can search about whether to use Umbrella projects or not beyond info from the official site. It’s a little deeper than just directory structure. Conveniences exist such as being able to run mix test or iex -S mix phx.server from the umbrella root and it will test or run all child apps at once. Also when deploying (mix release or distillery) you can create multiple releases with subsets/groupings of the child apps.

If it’s a simple web app with database and planning to deploy in one release then probably simplest to just do mix phx.new my_app and not use the umbrella to start out with. A great thing about Elixir is how easy it is to reorganize your code. If you later decide you want to add more apps to your project you can easily group apps under an umbrella project at that point.

2 Likes

I created a normal umbrella project.

mix new myprj_umbrella --umbrella

As one of the apps I created a normal Phoenix Live:

cd myprj_umbrella/apps
mix phx.new live_app --live

What I did to keep my Umbrella project intact was to comment the config_path in mix.exs regarding Umbrella reference:

  def project do
    [
      #config_path: "../../config/config.exs",

so that it uses the local default Phoenix config.

But still keeping and sharing the Umbrella structure for:

      build_path: "../../_build",
      deps_path: "../../deps",
      lockfile: "../../mix.lock",

Ant it worked. I could run Phoenix on the app structure:

> cd live_app
> mix ecto.create
> mix phx.server

I might be missing something, but it seems to be a solution, instead of creating a Phoenix Umbrella project.

I’m not sure why you’d complicate things with that structure instead of using the correct generator - mix phx.new.web will accept the --live option as well and generate a LiveView-enabled Phoenix application in apps.

I could also see problems with build_path disagreeing with config_path if code uses Application.compile_env to read config values at compile-time; depending on whether the umbrella or the live app compiles code, it could use different values in the generated BEAM file.

I agree that changing default settings is not a good aproach.

However, in my case I have a large distributed system having many apps.
Being the MMI/GUI using Phoenix just one of them.
Namely, it is not a Web project, I just use Phoenix to provide a Web GUI/MMI like for the system.

The system DB I also made as a separate app using Ecto.

Everything is working so far on DEV and Test Envs.
Using Nodes as well as direct Phoenix PubSub mechanism.
Everything still on a single host.

So far so good, but when it comes to release/deploy in Prod Env that decision might become another headache.

I am still far from that phase.