Imagine you create an app that you will sell to many different companies. It can be for example some application for managing company's finances or an app that will simplify holidays requests flow in a company. What is important is that you will sell that to different entities (companies, universities or whatever) and one client should not know anything about other clients that use your application. Your clients should not share users, data, pages or anything. You even probably want to have different URLs for different clients for example:
- client-a.app.com
- client-b.app.com
One way of doing that is duplicating your app instance for each client. Each instance can be put on different machine or they can share one physical machine but can be separated from each other using virtualization or using containers.
But hey - what if your app gets popular and you get thousands of clients? Does it mean you need virtual machine/container or real machine for each of them? Well... yes. This approach of splitting your clients is called "Single-tenancy" (or "Singletenancy" without a dash). Another approach is a "Multitenancy" (sometimes referred to as "multi-tenancy" with dash or "multi tenant"). This approach, in simple words, means to have different clients on same server. The clients share the same database and all the services. The clients are only separated from each other on database level by using for example some column which distinguish them.
I actually got some questions about multitenancy in Liferay from one of the clients I did some consulting for and I thought I will cover part of this topic in an article.
Note some sources also mention something "between" these two where there is a single app like in multitenancy approach but uses different databases (or database shards) for each client like in single-tenancy approach. In this article I will not talk about this approach too deeply. I will just give you some informations about Liferay support of multi databases in the end of this article. Beside that I will stick only to single-tenancy and multitenancy approaches for the rest of the article.
Single and multi tenancy comparison
So now you have the basic understanding of what single and multitenancy is. Lets compare some more aspects of these two.
We will start with things that are better with single-tenancy approach and then we will move to the aspects that are better in multitenancy.
Warning please note that I will not go through every aspect that might be important. There are other articles that might cover the topic more deeply. In this article I want to introduce you to the topic but more importantly I want to show you how multitenancy can be achieved in Liferay.
Data separation and security that comes with it
Probably the main benefit of single-tenancy over multitenancy is the fact that the data is really separated from each other. Even if there is some security issue with your app, client A will not see the client B data.
With multitenancy each client is on the same database so if there is any flaw in your app there is a chance that clients will see eachother data. This of course would be a huge issue for any application provider - not only because app doesn't look to good to its clients but also it might come with some legal issues for example with EU GDPR.
Different clients can have different versions of your app
Another benefit of single-tenancy might be the fact that you can have slightly different versions provided to each client. In some rare occasions it might be crucial for your app to have different modules versions for different clients. This approach is not recommended as it might get really hard to manage these different versions in the future. Nevertheless if you have just couple of clients then you might want to use that sometimes.
In multi tenant approach you have one server so you provide everything or nothing. This of course doesn't mean that you can't prepare per client solutions. You should simply consider other ways of providing these for example using so called "Feature flags". In Liferay's portlets you can also simply deploy your new portlets but don't put them on any page or even disable them per client (we will go into details of this later).
Deployment process
In single-tenancy deployment process will be more difficult. Of course as the number of clients increase you will need a good automatic deployment process (continuous delivery/deployment) but it still might cause you some issues if anything goes wrong and you have hundreds of clients (machines, virtual machines or containers).
With multitenancy there is one single server (or cluster) so deployment process is simplified but it comes with a cost actually - if there is an issue in your deployment it will affect all the clients. This is the aspect where both single and multi tenancy have their pros and cons.
Resources
Single-tenancy doesn't shine so brightly when it comes to resources though. Since you use container/virtual machine or even real machine for each client then you need more resources. Of course Docker is better solution than real machines when it comes to resources increase but it still comes with some cost.
With multitenancy you have only one server (or cluster of servers) so you can manage that better.
Providing high availability (HA)
Providing high availability (HA) in single-tenancy approach might be harder as each client has its own instance of an app - it might be impossible to create a cluster for each one of them as it would be expensive and hard to manage.
With multitenancy clustering is something you will just need to do at some point as more clients means more resources and and some point you will encounter limits of your server.
Multitenancy in Liferay
Now we had a little introduction to the topic of multitenancy lets see how this can be achieved in Liferay. In fact this is quite easy as it's supported out of the box in Liferay Portal with so called Virtual Instances. Virtual instance is just a virtual thing that lets you split your application. Each virtual instance has its own URL, its own users, organizations, roles, pages and even its own sites. There are also lots of per instance configurations.
Note I will use Liferay 7.4.0 CE in my examples but the settings I will show are quite similiar in all of the versions of Liferay, even in obsolote 6.2. Sometimes they are just hidden in other places of the control panel.
Instances routing & default instance
You might wonder - how Liferay knows which instance user should see when he enters your portal? Well it's quite simple. Each instance has it's own domain, or so called "Virtual Host". If user enters your app by this domain Liferay knows which instance should be served.
Another important term is a "Default instance". What is a default instance? Well by default it's an instance created automatically when you first started your Liferay Portal. This is also the instance that is shown to you if you enter your portal using IP address instead of the domain name.
Default instance is also like a "root" instance and most priviliged one - some config can be made only there for example changing system settings or configuring virtual instances. Because of these two you probably don't want to put any tenant on your default instance and simply use it as an administrative one. You can also consider blocking access to your portal by IP so no one has accidental access to this default instance.
If you use Liferay in single-tenancy way (with only one client) then you probably can stick to using only default instance.
It's also important to know that user can't check all the instances you have created unless he has administration role in your default instance.
Changing default instance
Sometimes you might need to change the default instance of your Portal. This option is a little bit hidden and I couldn't easily find any informations regarding that in the Liferay's docs. This is possible though using portal.properties config and the key you're looking for is "company.default.web.id" with default value set to "liferay.com"
company.default.web.id=liferay.com
I assume you're familiar with portal.properties. If not then you can read some more about it, and its extension in Liferay docs.
Instances panel & creating new instance
If you want to see all of your instances you need to go to the:
Menu button -> Control Panel -> Virtual Instances (under System)
First open menu button
And then in the panel that shows up select Control Panel -> Virtual Instances
Note this page is only available on default instance of your Liferay Portal.
Once you open your virtual instances panel you will see table like on the screen below:
To create new instance you simply click the blue plus button. New page will open: There you simply need to fill few fields:
- Web ID - something unique, as it's the id of the instance. Often it's just a domain name
- Virtual Host - the most important field which tells Liferay which domain should be redirected to this instance
- Mail Domain - used for emails notifications from the instance
- Max Users - maximum number of users for this instance. You can use 0 for "unlimited"
- Active - either the instance is active. If it's not then it's not accessible by anyone
After filling everything you confirm using "Save" button.
To test your new instance you need to go to the Virtual Host domain. If you don't have real domain for testing you can use so called "hosts" file to redirect some fake address to your local machine. You can find more info on that topic for example in this article.
In this case that's exactly what I did - I changed my hosts file to point the app.pydyniak.com to my local machine:
127.0.0.1 app.pydyniak.com
The default user is called "test" and its password is also "test" and the email domain is the domain you used for "Mail Domain" field during your instance creation.
You might also notice that your admin user from default instance doesn't work on your new instance - like I mentioned before almost everything is separated per instance and that includes users, even the admins.
From now on you can start using your new instance totally independently.
Things shared between instances
Of course there are some things shared between instances. Probably you can thing of some based on the previous part of the article but I will also point few most important:
- Database - all instances share the same database (later I will also write few words about using different databases/shards per instance)
- Server - obviously if you use one portal you use same server. Virtual instances are just a partitioning capability
- Installed modules/themes - but OSGI portlets, themes and layouts can be disabled per instance. I will go into more details of this later on.
- System settings - the top level settings in Liferay are the system settings. They're common for all the instances but some of them can still be overriden per instance and they only act as default unless overriden
- Marketplace plugins
- Gogo shell - as modules are common so is the gogo shell. It includes both gogo shell available through control panel as well as the gogo shell access using your terminal
- Search engine - some search can be configured per instance (fields mappings) but in general same search engine (Elasticsearch) is used across all the virtual instances. Note that fields mappings are configured per instance but the configuration can only be made on default instance.
- Log levels
Things above are probably the most important things that all virtual instances share. It's also important to know that in general the options that are shared are only configurable through default instance.
Things not shared between instances
Almost everything not mentioned in the previous point is not shared between instances. It includes:
- Sites
- Pages
- Users, roles, user groups, organizations
- All the content including web contents, templates, structures, categories, tags, widget templates, fragments, documents & media and everything else you can think of connected to content
- A lot of instance settings (under control panel -> Instance settings)
- Active components - I will go into more details of this in a while
Disabling Liferay components
Like I said before: in Liferay OSGI modules are shared between all instances. This is connected to the fact that one server has one common place where it stores all the modules (OSGI ones, themes, layouts etc). So if you deploy something new to your server it's automatically available on all virtual instances. Of course the fact it is available doesn't mean you have to use these. You can simply not put your portlet or any page or don't set your theme for any page.
They're still visible though but there's is a solution for that. If you decide that you don't need some components you can actually disable them and that can be done per instance. To do that you need to go to control panel -> components panel of your instance. In this panel you will see three tabs:
- Portlets
- Themes
- Layout Templates
In each of these tabs you have a list of components with their status:
You might notice that sometimes there are two components with same name like "Accounts" on the screen above. To get some more details you can go into details by clicking on the three dots on the right: And in that panel you can check the Plugin ID to see what is the difference between components. You can also disable the component in that panel by unchecking "Active" checkbox and you also a little summary of permissions connected to this component: Likewise you can go to the details of themes or layouts.
There are many different scenarios where you might want to disable some of the components. For example:
- You want to disable some default Liferay portlets you don't need - for example you might not need Liferay Commerce modules in your intranet instance
- In your SaaS you might want to disable components client didn't pay for so he can't find them and put them on the page without your knowledge - of course make sure that client doesn't have admin account and can't go to components panel himself :) You can always create a "partial admin" role without this specific permission
- You have only one or two themes/layouts you want to use across all your instance and you don't want someone to use something not meant for the instance
Please note that of course OSGI modules are still shared and you can only disable some types of components. In some cases you need some extra logic in your code to make sure that it doesn't affect all the clients and only the ones you choose. Such examples can be CRONs or Servlet (Portlet) filters.
Per instance database/sharding support in Liferay
I will not go too deeply about sharding as this is totally out of the scope of this article. If you don't know what sharding is you can use your favourite search engine to find out some more or, if you are not interested in using different databases with one portal, you can simply skip that part of the article.
Some people might wonder if there is any out of the box sharding or multiple databases support in Liferay. If you look for that on the internet you might actually find some old articles how to do that but unfortunately they're outdated. For the moment there is no functionality like that in Liferay 7. Back in the 6.x versions you could define multiple database sources and for each instance you could pick which one to use. For example instances A, B, C could use one database and instances D, E, F could use another one.
This is no longer possible as stated in docs.
Instead you need to stick with one database source and use sharding/replication options provided by your database vendor.
Other use cases for Liferay's Virtual Instances
Please note that achieving multitenancy is just one of the things you can do with Virtual Instances. Depending on your business goal you might have other reasons to use them even if you use it only for a single company. In most cases though sites are the natural choice for single company projects.
Conclusion
I hope this was not too long and at least a little bit interesting. Quite often in Liferay many topics, even the popular ones are not covered enough by the documentation and if you look for them you might find a lot of outdated articles. This is actually the case with multitenancy topic - a lot of articles are written for Liferay 6.x and there are not too many resources about 7.x. Of course you don't want to use Liferay 6.x anymore and there are lots of reasons for that. If for some reason you still use it you should definitely consider updating it - I wrote some articles about the issues you might encounter in Why migrating Liferay projects to a newer version is not easy article and the second part of it. Also if you need any help with migration you can always contact me - migrations are my most popular consulting projects.
I hope this article will help at least some of you.