Phoenix for a multisite setup?

I wonder if there is any way to use the phoenix framework for a multisite setup. I suppose it is not included but is it possible without to much hassle to build a solution that can route different domains to soecific sites.

1 Like

Like a reverse proxy?

1 Like

Some time ago I have created Dolores

It is meant to be used as local development reverse proxy that allows testing against multiple domains as well as provide hassle-free TLS setup. It is still WIP, but is more or less usable in current state.

Alternatively you can edit /etc/hosts or use local DNS resolver that will resolve all domains for given TLD (I suggest .localhost) to the ::1/127.0.0.1, so you will be able to test different domains without any trouble.

4 Likes

I guess l am too inexperinced to know if l mean a reversed proxy. I would like to build something similar to a wordpress multisite cms. I think elixir and phoenix seems very interesting but l am not sure if phoenix can be built into a multisite cms. I would like to build a system where all sites share the templates so that each site has a unified look. Each site shuld be able to create new pages based on these templates but with unique content. Now would it somehow be possible to map x.com in to a specific site and y.com in to another site in phoenix. Could this idea be built with phoenix?

1 Like

You want a multitenant application, and yes, you can build it with Phoenix, a lot of SaaS do it, most of them use subdomains to host the same site with different content for each tenant like client1.example.com client2.example.com and so on.

You can use Triplex as a starting point and use the Subdomain Plug, then you can use CNAMEs to map the subdomains of your app to domains (or you can write your own Plug mapping domains to tenants).

4 Likes

You could do a WordPress like multisite using subdomains, umbrella apps, and Triplex. I am currently working on SaaS app that does just that.

This article helps get started with subdomains Subdomains With Phoenix - Gazler.

The umbrella apps are a little tricky but work well in the end because you can have subdomains in its own app and with its own views and templates and have it communicate with the parent umbrella app that handles schemas and context.

You can then use Triplex to handle schema prefixes for you subdomains.

3 Likes

Yes this could be built with Phoenix. A rough idea of what you’ll need:

Handling SSL
SSL for subdomains is pretty straight forward because you can just run certbot or something similar once to get a wildcard cert for *.yourdomain.com.

Handling SSL for apex domains (no subdomain) is trickier because:

  1. The DNS needs to be pointed at your server to complete an SSL challenge and get a cert.
  2. Since the domain is not under your control you need to either keep checking their DNS, retry periodically (risks hitting lets encrypt’s low limits), or ask the user to click retry when they’re sure it’s ready (prone to user error and propagation not being finished).
  3. As mentioned above, Let’s Encrypt has fairly low limits and if you try to get too many certs or make too many attempts too quickly, you can get locked out - I think a week is the default lockout :grimacing:

You’ll probably want a reverse proxy in front for apex domains

Apex domains are domains without any subdomain. They can only be pointed with A records at an IP address or Alias records can point at another domain/subdomain (many DNS providers don’t offer Alias records yet though).

Handling SSL is generally easier if you use a reverse proxy because things like certbot work well with them and you won’t need to restart or update your SSL config (or even have any at all) in your phoenix app. The problem with most reverse proxies like nginx and apache are that they’re not easy to automate.

I can recommend Caddy server as an alternative because it has something called TLS on demand that will wait until the first request comes in for a domain/subdomain, and then it’ll generate a complete SSL cert automatically before returning a response.

If all of the custom domains/subdomains are reverse proxying to the same app, you can use a wildcard matcher to accept any domain, and Caddy has a feature that will check a domain to see if it’s allowed to generate a cert. That lets it prevent unwanted domains spamming your server with cert creation and using up your cert limits.

If they need to go to different apps, you can use the Caddy admin API to generate virtual hosts on the fly.

You’ll need to handle requests from different domains/subdomains in the phoenix app

You’ll need something that can grab the host header (domain/subdomain) from the request and then have your phoenix app return a response that’s appropriate for that domain/subdomain.

For example if you had a blog service and customerdomain.com was suppose to load their specific blog home page, you could intercept the request to the route for /, grab the content for the page from the db and then return it instead of your default content for /.

One way to do this for subdomains would be the Triplex subdomain plug that thomas.fortes mentioned, but you could also write your own plug or handle it at various different levels (controller, context, etc).

Global Distribution
I’m guessing most people don’t have this right now, though more and more will as options like fly.io (which is fantastic) become more widely used. Still though, there can be some traps so I’ll mention them.

Let’s say you’ve setup all of the above and you have a nicely automated custom domain/subdomain system running. But it’s running on one server out of US east and your customers overseas are complaining of really high latency. The solution is to distribute your app globally (wohoo OTP!). So you go and do that on something like fly.io and now you have your app running in 5 different locations worldwide.

The problem here will be SSL cert synchronization. Your 5 different VMs as they are will all have to regenerate a cert independently for the same domain because they aren’t synchronizing certs at all yet. To do that you’ll want to look into the caddy redis module (may be other options in the future, but that’s the best right now).

The caddy redis module basically lets you setup a redis instance that all of the caddy instances can connect to and share ssl certs. That goes a long way to avoid hitting any cert generation limits, and also prevents any odd issues with browsers caching a cert and then getting a different cert the next time they visit because they’ve hit a different server.


So off the top of my head those are the main things you’ll need to sort out when doing custom subdomains and apex domains. Feel free to DM me if anyone is struggling to sort any of this out, it can be a lot. Happy to help you figure out your own solution.

Shameless plug here, I built a service with an API, approximated.app, that handles all of this except the part inside of your own phoenix app. It’s built with elixir/phoenix, caddy, and fly.io for people who’d rather just pay a bit to not have to build and maintain all of the stuff listed above.

4 Likes

Thank you all for your answers!!! Will have some studying to do!)

Interesting. We rolled our own multi-tenanted stuff because we had a really weird setup (Postgres database per tenant instead of schema per tenant). We’ve since moved to something more sane (schema per tenant), but we still have six different Postgres servers (each with about 200 tenants).

The current version of Ecto almost has robust multi-tenanted support built-in. Between c:put_dynamic_repo/1 and c:default_options/0, all we had to do was write migration tasks (which run in parallel across multiple tenants, which is super nice).

Oh, and Tenant.set/1 and Tenant.get/0, which are pretty easy using $callers and $ancestors on processes.

I feel like Ecto is so close to supporting robust multi-tenancy out of the box. It’s just a matter of making migration tasks aware of when a repo is using the dynamic repos feature, and maybe giving a callback to list out what all the dynamic repos (and tenant schemas) are.

4 Likes

back in the days l used wordpress and built an admin interface for it and for each new site we launched a bash script that created a new nginx server snippet and ran certbot on the apex domain as soon that it was pointing to our server.

When l read the comments above it at least seems so complicated with the pointing of domains, reverse proxies etc.

Is the approach that we used back then not a good way to handle ssl and domains etc in your opinion?

That approach with nginx sounds okay to me, nothing wrong with it. Doing it that way is using a reverse proxy (nginx) and every different method requires pointing domains with DNS, including when you use to use the bash script, so nothing new there. I gave a lot of info for completeness sake so it may have sounded more complicated than it needs to be.

The main tripping points with using your old method would be your app knowing when to run certbot, after they’ve pointed the domain as you mentioned. That and making sure you it doesn’t go over the let’s encrypt rate limits.

1 Like

Ok cool, thanks for your input!