fepli
Hostingenterprise

Single or multi-tenant

One fepli installation can serve one organisation or many. Each organisation is a tenant, with its own domains, admins, settings and data. This page explains the two setups and how to create the first tenant and the first admin, which can only be done on the command line.

Tenants

A tenant is one organisation that uses fepli, such as a municipality with its holiday programme, a club or a library. It has:

  • a slug, its machine name, e.g. musterstadt,
  • a name, e.g. "Ferienpass Musterstadt", which also appears as the sender of its e-mails,
  • its domains,
  • its own admins, hosts, families, offers, settings and CMS pages.

All tenants of an installation share one database, and fepli keeps their data apart. A family registered with Musterstadt doesn't exist for Beispielhausen, and an admin of one tenant can't see the other. In the CMS, which all tenants share, a tenant's super admins edit their own website only: see Give the tenant its website.

There is no separate single-tenant version: every image is the same. Whether an installation is single- or multi-tenant only depends on how many tenants you create, and on whether you switch on the platform console.

Single-tenantMulti-tenant
Tenantsoneas many as you need
Adminat /admin on the organisation's domainat /admin on each tenant's domain, plus the platform console
Tenants are createdonce, on the command lineby global admins in the platform console, or on the command line
FERIENPASS_PLATFORM_HOSTnot setthe console's domain

Start with a single tenant if you are unsure. You can add a platform console and more tenants at any time, without moving data.

Domains

Every tenant claims the domains it is reached on. A domain serves one of two things:

  • A public host serves the tenant's website, and its admin under /admin. This is the usual case: https://example.fepli.eu and https://example.fepli.eu/admin.
  • An admin host serves only the admin, at the root of the domain, without /admin. Once a tenant has an admin host, its public hosts stop serving the admin.

Two more kinds of domain serve no request at all, so they don't go into TRUSTED_HOSTS: the domain a tenant's e-mails are sent from (E-Mail-Versand), and the domain replies to its messages arrive at (E-Mail-Empfang, see E-mail replies).

A request for a domain no tenant claims is answered with 404, before anything else happens. fepli never falls back to a default tenant. So when you add a domain, add it in two places: to the tenant, and to TRUSTED_HOSTS.

Links in e-mails lead to the domains of the tenant that the account or record they are about belongs to. That holds for sign-in links, invitations, the link to set a new password that an admin sends, the link that confirms a new sign-in address, and every link in an e-mail the worker writes, wherever the e-mail was sent from: the platform console, an API domain or another tenant's domain included. A link into the admin goes to the tenant's admin host where it has one, otherwise to /admin on its public host. A link to the website, such as a family's sign-in link, goes to the public host. APP_BASE_URL is only the fallback, for a link without a tenant or a tenant without a domain for it.

In notification texts, {{ baseUrl }} is the address of the host the e-mail was sent from when that is one of the tenant's public or admin hosts. Sent from any other host, it is the tenant's public host, or its admin host if it has no public host.

Giving a tenant its first admin host moves its admin: from then on, /admin on its public hosts answers 404. Invitations and sign-in links sent before point there and stop working. Send open invitations and sign-in links again once the admin host is in place.

The platform console

On a multi-tenant installation, the platform console is the place to manage the installation itself. It has a domain of its own, e.g. platform.example.fepli.eu, set with FERIENPASS_PLATFORM_HOST. It belongs to no tenant and serves only the admin, at the root of the domain.

Only global admins can sign in there. They see:

  • Installation → Mandanten: the list of tenants, with Mandant anlegen to create one, and the switch to take a tenant offline.
  • Wechseln: stepping into a tenant, to work in its admin.
  • CMS in the top bar: the CMS of the tenant they switched into, on its public host, without signing in again. There they administer the whole CMS. The tenant's log under Betroffenenrechte records CMS dieses Mandanten geöffnet, and Admin-Panel in the CMS leads back to the console.
  • Installation → Systemwerte: the values all tenants share, such as the installation's sender address (Absenderadresse der Installation) and the maintenance mode, which puts every tenant's website behind a maintenance page. The admin stays reachable, and so do the files it offers for download.

On a new installation without tenants, the console shows nothing but the tenant list, until the first tenant exists.

Run commands in the container

The first admin is created with a console command inside the web container. With Docker Compose, prefix every command on this page with docker compose exec web:

Run a console command

docker compose exec web php bin/console ferienpass:tenant:list

Other platforms have their own way to open a shell in a running container (kubectl exec, the terminal of Dokploy or Coolify, …). Run the commands from /app, which is where the container starts.

Set up a single tenant

Create the tenant with its public hosts, then its first admin:

Create the tenant

php bin/console ferienpass:tenant:create musterstadt "Ferienpass Musterstadt" \
  --host=example.fepli.eu \
  --host=www.example.fepli.eu

Create the first admin

php bin/console ferienpass:user:create anna.schmidt@example.fepli.eu \
  --tenant=musterstadt --firstname=Anna --lastname=Schmidt

The second command asks for the password twice. The admin is a super admin of Musterstadt: they may do everything in its admin, including payments, participants and the CMS, and invite everyone else from there. They sign in at https://example.fepli.eu/admin.

Then give the tenant its website.

Set up several tenants

  1. Set FERIENPASS_PLATFORM_HOST to the console's domain, add that domain to TRUSTED_HOSTS, and restart the containers.

  2. Create a global admin. It belongs to no tenant, so it needs no --tenant:

    Create a global admin

    php bin/console ferienpass:user:create you@example.org --global-admin
    
  3. Sign in at https://platform.example.fepli.eu and create the tenants under Installation → Mandanten → Mandant anlegen, with their domains. Or create them on the command line with ferienpass:tenant:create, as for a single tenant.

  4. Give each tenant its first admin: switch into the tenant and create its admins there, or use ferienpass:user:create with --tenant.

  5. Give each tenant its website.

Global admins have the rights of a super admin in every tenant they switch into. The personal data of the tenant's families stays masked for them by default. Keep their number small.

The commands

  • Name
    ferienpass:tenant:create
    Type
    slug name
    Description

    Creates a tenant. The slug is kebab case (musterstadt, bad-musterbach) and can't be changed later. --host adds a public host, --admin-host an admin host. Both can be repeated.

  • Name
    ferienpass:tenant:list
    Description

    Lists the tenants: slug, name, public and admin hosts, and whether they are online.

  • Name
    ferienpass:user:create
    Type
    email
    Description

    Creates a super admin. --tenant=<slug> names the tenant it belongs to, --global-admin makes it an admin of the installation instead. --firstname and --lastname are optional.

Passwords:

  • Run interactively, ferienpass:user:create asks for the password.
  • Run without a terminal, e.g. from a script or with docker compose exec -T, it generates a password and prints it once. Change it after signing in.
  • --password=… works too, but leaves the password in your shell history and the process list. Prefer the prompt.

An e-mail address is unique per tenant, not per installation, so the same person can be an admin of two tenants with two accounts.

Give the tenant its website

The public website of a tenant is a page tree in the CMS, which the admin opens under CMS in its top bar. Each tenant's tree starts at exactly one root page, and the root page says which tenant it belongs to. Until the tenant has a published root page, its domain has no website, though its admin works. A tenant with only an admin host has no website and needs no root page.

The CMS has no Template Studio: fepli's templates come with the image.

One tenant

On an installation with one tenant, its super admins administer the whole CMS. They create the root page themselves:

  1. Sign in to the tenant's admin and open CMS in the top bar.
  2. Go to Seiten and create a page of the type Startpunkt einer Webseite.
  3. Set its Domainname to the tenant's public host, e.g. example.fepli.eu, its Sprache to de, and choose the tenant under Mandant.
  4. Publish it, and build the pages below it.

Several tenants

Once an installation has a second tenant, disabled tenants included, the CMS keeps the tenants apart. A tenant's super admins then edit:

  • the pages and articles of their own website, the root page included, but not the tenant it belongs to,
  • the files in the tenant's own folder, files/<slug>, e.g. files/musterstadt,
  • the tenant's forms and FAQ categories, with their fields and questions. They can create and delete both, but can't let a form store what it receives in a database table.

A form or FAQ category belongs to one tenant, or to the installation. A new one belongs to the tenant it is created for: the tenant of the super admin who creates it, or, when a global admin creates it, the tenant whose public host the CMS runs on. Global admins hand it to another tenant, or to the installation, under Mandant, a field only they see.

Themes, layouts and front end modules belong to the whole installation, and only global admins change them. A tenant's super admins can't use the content elements Ungefiltertes HTML, Inhaltselement, Artikel and Artikelteaser; elements of these types already on their pages stay, but they can't change them.

A tenant's super admins can't add root pages either. So the first time one of them opens CMS from the tenant's admin, fepli prepares the website:

  • An existing root page that names no tenant and carries one of the tenant's public hosts becomes the tenant's.
  • Otherwise fepli creates a root page for the tenant: named after it, on its first public host, with the Sprache de and the first layout of the installation, and not published.
  • fepli creates the folder files/<slug>.

The super admin then publishes the root page and builds the pages below it.

Each time one of them opens CMS from the tenant's admin, forms and FAQ categories of the installation become the tenant's where they clearly belong to its website: a FAQ category whose reader page is part of it, and a form that only articles of its website show. Anything less clear stays with the installation, such as a form that another website or a front end module shows: a global admin assigns it, or copies a shared form for each tenant. Forms and FAQ categories from versions before they had a tenant belong to the installation at first, and find their tenant the same way.

A tenant's super admins get these rights on their next request in the CMS after the second tenant was created. Files uploaded before stay in use on their pages, but the file manager shows them only what lies in files/<slug>.

Was this page helpful?