How can you run multiple applications on one BEAM instance with MainProxy?

Hi @axelson, thanks for putting together main_proxy. I want to use it to do the same thing you’ve used it for: running multiple applications from one BEAM instance. I have an umbrella application with three phoenix endpoints (3 separate child apps): one for an e-commerce shop, another for the store’s admin area, and a third for its cms admin area. I want to route the requests for the admin areas to the single exposed web port for the e-commerce shop since the other two will never see much traffic. However, I’m having difficulty getting it to work with my release.

I have the functionality working correctly in both my local dev and prod environments, but once I use mix release to produce a release nothing is served when I start it. I’m seeing logs such as Configuration :server was not enabled for MyAppWeb.Endpoint, http/https services won't start for each web app that I’m trying to spin up locally in my release. Upon deployment (we build & start the release on Render) I’m seeing those logs plus the following ==> No open HTTP ports detected on 0.0.0.0, continuing to scan...

Do you have any idea what the issue could be?

You probably don’t have this part in your config/runtime.exs:

config :main_proxy,
  server: true,
  http: [
    port: 8080,
    ip: {0, 0, 0, 0}
  ],
  # ...

Change the port accordingly.

1 Like

Hey @stefanluptak thanks for the suggestion. I had port already in config/config.exs and I’ve added ip (tried them in both config.exs and runtime.exs), but I’m still running into the same error logs.

Two suspects:

  1. Are you starting MyApp.Proxy (or whatever you called it) in your application.ex in the supervision tree? This is this step in the readme:
children = [
  # ... other children
  MyApp.Proxy,
]

This should be done in one of the applications you have in app/. In my case I created a separate application just to serve that.

  1. Are you including and starting the application mentioned in the paragraph above (may be your “ui” or “web” app too if you don’t use dedicated one for proxy) in the release configuration?

You should have something like this in the top-level mix.exs of your project

deps: deps(),
releases: releases()
...

defp releases do
  [
    my_app: [
      applications: [
         proxy: :permanent
         ...
       ]
    ]
  end

I strongly suspect you’re not doing 1 or 2 or both and the OTP application that should have main proxy as a child either does not start at all, or does not start the child.

2 Likes

I am also suspecting not exposing ports in Docker, if it is being used.

1 Like

Hey @hubertlepicki thanks! #2 fixed the release locally and I’m now seeing the other two apps when I start it. The app deployed correctly and I’m no longer seeing the No open HTTP ports detected logs. Unfortunately the admin apps still aren’t being served according to the backends I defined in MyApp.Proxy. What could I be doing wrong there?

I’m not using Docker.

Do you include these admin apps in the release as well? They both should have entries with :permanent just like your main app does.

1 Like

Yea one entry for the main app, one each for the admin apps, and now one for the proxy app.

And it’s broken as well on the local release and when you deploy, or only when you deploy?

But it works when you start with iex -S mix ?

Also: what does it mean it’s broken. It renders 404 page out of the main app?

1 Like

Only when I deploy. The local release is starting normally and I can access the admin apps from the domains defined in backends. On the deployed version I’m seeing a “This site can’t provide a secure connection my-shop-admin.my-app-lhrl.onrender.com uses an unsupported protocol.” page (ERR_SSL_VERSION_OR_CIPHER_MISMATCH). It doesn’t actually produce a log with more information.

This does look like a problem on Render.com rather than in Phoenix already. I’ve never used it myself. I suspect now the Phoenix app works and main_proxy too as expected but there’s some additional problem with generating SSL certificate or serving it over render.com

1 Like

Yea I think you’re right. I’ve removed my https configurations for main_proxy after reading about their policy on TLS (SSH) certificates and this post in their community. I’m going to try reaching out to their support team about it. If things don’t work out with Render, which service would you suggest using for this sort of setup with main_proxy?

I was using it with both Heroku and Gigalixir, also have the app now moved to Google Kubernetes Engine. This library is pretty deployment-agnostic.

1 Like

I think there could be a mismatch between your backends specification and the custom domains (with certificates) you set-up in the render.com settings for your app.
EDIT: I ran few apps with MainProxy on render.com successfully.

2 Likes

So you’re saying that I need to have the actual custom domains configured in the backends specification, @stefanluptak?

Right now I’ve got the backends configured to use the domain from the RENDER_EXTERNAL_HOSTNAME env variable that Render provides for their provisional onrender.com domains (I haven’t got to setting up the custom domain yet).

Could you provide your main_proxy backends setup?

1 Like
defmodule MyAppProxy.Proxy do
  use MainProxy.Proxy
  @render_external_hostname Application.compile_env!(:my_app_proxy, :render_external_hostname)

  @impl MainProxy.Proxy
  def backends do
    [
      %{
        domain: @render_external_hostname,
        phoenix_endpoint: MyAppWeb.Endpoint
      },
      %{
        domain: "my-cms.#{@render_external_hostname}",
        phoenix_endpoint: MyCmsWeb.Endpoint
      },
      %{
        domain: "my-admin.#{@render_external_hostname}",
        phoenix_endpoint: MyAppAdminWeb.Endpoint
      }
    ]
  end
end

If the RENDER_EXTERNAL_HOSTNAME is app-123.onrender.com for example, then it means that for MyCmsWeb and MyAppAdminWeb to work, domains my-cms.app-123.onrender.com and my-admin.app-123.onrender.com would have to exist, but that’s not the case, because you’re not able to create DNS records for onrender.com subdomains.

If you don’t want to use your own custom domain, you should probably use :path instead of :domain.
But I suggest to set up your custom domain. Then save it’s value to a CUSTOM_DOMAIN env var for example and use that one in your MyAppProxy.Proxy code instead of the RENDER_EXTERNAL_HOSTNAME.

3 Likes

Ok I gave :path a try, but the problem I’m running into now is that the cms is taking up all the spare paths. That is to say all the paths which aren’t already defined in the router file as routes for the app’s functionality are reserved for the cms. There’s probably a way to skirt around this, but I’d prefer not to complicate things further.

I think the only way forward is to use a custom domain as you suggested, but I was hoping to test things out with Render’s provided onrender.com subdomain first. I’ll continue to experiment with this and I’ll drop an update here if there’s anything useful to share. Thanks for your help @stefanluptak!

2 Likes