Configuration
fepli is configured with environment variables, and only with them. The same set goes to all three containers that run the image, the web, the worker and the cron.
Give the web, worker and cron containers exactly the same variables. The worker writes the e-mails and the cron sends the reminders: a worker with a different database or mail server than the web container sends the wrong mail, or none. An env file shared by all three, as in the examples, is the simplest way.
Required
Set all of these in production. Without them, fepli doesn't start, or starts with defaults that are wrong for you.
fepli refuses to run while APP_SECRET is empty or a placeholder published with fepli or Symfony, such as ThisTokenIsNotSoSecretChangeIt or !ChangeMe!, and while INTEGRATIONS_ENCRYPTION_KEY is empty or a key published with fepli. The values earlier versions of the local setup used count as published for both. Requests then get an error page, and commands stop, the database migration at start among them. The message in the log names the variable to set. Only the dev and test environments are exempt; the image runs as prod.
- Name
APP_SECRET- Type
- string
- Description
Signs sessions, login links and form tokens. The image sets none, so every installation sets its own:
openssl rand -hex 32. Changing it later signs everyone out and invalidates the links in e-mails already sent.
- Name
INTEGRATIONS_ENCRYPTION_KEY- Type
- string
- Description
Encrypts the credentials admins store in the admin, such as a payment provider's access data. The image leaves it empty, so every installation sets its own. Generate it once with
openssl rand -base64 32and never change it: credentials stored with another key, an empty one included, can't be read anymore, and admins have to enter them again.
- Name
DATABASE_URL- Type
- URL
- Description
The MariaDB database, e.g.
mysql://fepli:secret@mariadb:3306/fepli. URL-encode special characters in the password (@becomes%40).
- Name
DATABASE_SERVER_VERSION- Type
- string
- Description
The version of your MariaDB server, e.g.
mariadb-12.3.0. It has to match the server you run, or the database layer generates SQL for the wrong version. The default ismariadb-11.2.4.
- Name
TRUSTED_HOSTS- Type
- regex
- Description
Every host fepli answers on, as one regular expression, e.g.
^(www\.)?example\.fepli\.eu$. With several tenants, join their hosts with|, and add the platform console. Production must set it: fepli builds the links in its e-mails from the host a request names, and an empty value accepts every host.
- Name
APP_BASE_URL- Type
- URL
- Description
Scheme and fallback host for links in e-mails, e.g.
https://example.fepli.eu. Links in e-mails go to the domains of the tenant they are about, wherever the e-mail was sent from, as described under Domains. A link gets the scheme and host ofAPP_BASE_URLonly where fepli knows no tenant for it, or the tenant has no domain for it.
- Name
MAILER_DSN- Type
- DSN
- Description
The outgoing mail server, e.g.
smtp://user:password@smtp.example.org:587. Usesmtps://for port 465. The image has no local mail server, so the default doesn't work.
- Name
ADMIN_EMAIL- Type
- Description
The envelope sender of every e-mail, which is where bounces go, and the address of the CMS, e.g.
ferienpass@example.fepli.eu. Your mail server has to be allowed to send for its domain (SPF, DKIM). The image sets none: without it, e-mails have no envelope sender of their own, and bounces go to the address they were sent from. Each tenant sets the sender name and reply address its families see in its own admin.
Services
How fepli reaches Redis and, if you run it, Varnish. Redis is used for four things, each in a database index of its own. The values below assume the services are called redis and varnish, as in the examples.
| Variable | Example | What it is for |
|---|---|---|
REDIS_URL | redis://redis:6379/1 | the cache of the tenants' settings |
LOCK_DSN | redis://redis:6379/2 | locks, e.g. so two families can't book the last place at the same moment. The default, flock, only works within one container. |
SESSION_DSN | redis://redis:6379/3 | the sessions of everyone signed in. Empty keeps them in files inside the web container: that serves one web container only, and an update signs everyone out. Set Redis to keep sessions across updates and to run more than one web container. |
MESSENGER_TRANSPORT_DSN | redis://redis:6379/messages | the queue of background jobs, read by the worker. The database index goes into the query string (?dbindex=0), not into the path. |
VARNISH_HOST | varnish:80 | where fepli sends purge requests when data changes. Set it empty to run without Varnish: fepli then sends none. The default is varnish:80, so leaving it out is not the same as setting it empty. |
VARNISH_BASE_URL | http://varnish | the base URL of those requests. This is the default. Without Varnish it doesn't matter. |
VARNISH_PURGE_TOKEN | the output of openssl rand -hex 32 | optional: the secret Varnish and fepli share for purges. fepli sends it with every purge request, in the header X-Purge-Token. With a value in its own environment, Varnish accepts purges only from the private network and only with that value; without one, any request from the private network may purge. So give Varnish and the web, worker and cron containers the same value. With the token on Varnish alone, every purge fails with 405. |
Redis must keep its data on disk and must never evict keys: an evicted job is an e-mail that never goes out, an evicted session is someone signed out in the middle of applying. Start it with --appendonly yes --maxmemory-policy noeviction.
Trusted proxies
- Name
TRUSTED_PROXIES- Type
- string
- Description
The addresses the proxies in front of fepli connect from, Varnish and your TLS proxy, as a comma-separated list of addresses and ranges. fepli takes the visitor's address from
X-Forwarded-Forand the scheme fromX-Forwarded-Protoonly in requests from these addresses. The default,127.0.0.1,::1,172.16.0.0/12,192.168.0.0/16, covers the networks Docker gives a Compose project. Set it where your proxies connect from elsewhere, e.g.10.0.0.0/8on Docker Swarm or Kubernetes. Empty trusts no proxy.
Multi-tenant installations
- Name
FERIENPASS_PLATFORM_HOST- Type
- host
- Description
The domain of the platform console, e.g.
platform.example.fepli.eu. Leave it unset on a single-tenant installation. Add the domain toTRUSTED_HOSTSas well.
Object storage
fepli keeps what people upload and what it exports on the local disk, in the storage volume. Name a bucket in S3-compatible object storage, and that part moves there instead. Without these variables, or with them empty, everything stays on the disk: an installation without object storage needs no configuration for it.
- Name
S3_FILES_BUCKET- Type
- string
- Description
The bucket for files that are kept: attachments, and each tenant's logo and sign-in picture. Include it in your backups.
- Name
S3_EXPORTS_BUCKET- Type
- string
- Description
The bucket for exports and the files attached to a Rundmail. fepli deletes Rundmail files after 7 days and builds an export again when it is missing, so give the bucket a rule that expires objects after 7 days or more.
- Name
AWS_S3_ENDPOINT- Type
- URL
- Description
The URL of your object storage, e.g.
https://s3.example.fepli.eu.
- Name
AWS_S3_REGION- Type
- string
- Description
Its region.
- Name
AWS_S3_ACCESS_ID- Type
- string
- Description
With
AWS_S3_ACCESS_SECRET, a key that may read, write and delete in both buckets.
Each bucket variable decides on its own: with only S3_FILES_BUCKET set, exports stay on the disk. What goes where:
| What | Bucket | Keys |
|---|---|---|
| attachments of offers | files | attachments/… |
| attachments of e-mail replies, files families send with an application | files | inbound_attachments/… |
| a tenant's logo and sign-in picture | files | branding/<tenant>/… |
| exports, files attached to a Rundmail | exports | at the root |
| images of offers, organisers' logos | none | always on the disk: fepli resizes images from local files only |
On a multi-tenant installation, <tenant> is the tenant's UUID, so each tenant's logo and picture have a folder of their own. A single-tenant installation uses default. The other keys are not split by tenant: which file belongs to which tenant is kept in the database.
Naming a bucket doesn't move the files already on the disk. Copy them first, each directory under storage/ to its keys in the table (storage/attachments/ to attachments/ in the files bucket, storage/export/ to the root of the exports bucket), and set the variable afterwards: fepli looks for a file only where its variable points.
Optional
Features that are off until you set their variable. Keys set here apply to every tenant; for several of them, admins can also store their own in the admin, per tenant, under Einstellungen → Integrationen. A key set here wins over the one in the admin.
| Variable | Switches on |
|---|---|
MAPBOX_TOKEN | maps: the meeting point on an offer's page, the location picker in the admin |
PMPAYMENT_AGS, PMPAYMENT_PROCEDURE, PMPAYMENT_SALT | online payment through pmPayment, with one account for the whole installation |
BREVO_API_KEY, BREVO_DSN | text messages through Brevo. BREVO_API_KEY also registers the webhook for delivery reports. |
INBOUND_SECRET, INBOUND_DOMAIN | e-mail replies: replies to a message from the admin arrive in its conversation. INBOUND_DOMAIN only on a single-tenant installation; with tenants, each names its reply domain in the admin. |
OPENAI_API_KEY | the AI features of the admin, such as generating offer images |
DEEPL_API_KEY | machine translation through DeepL |
CORS_ALLOW_ORIGIN | calls to the REST API from other websites' browsers, as a regular expression of their origins. By default only localhost may. |
STATUS_HEARTBEAT_URL | an hourly request from the cron to this URL, for an uptime monitor that raises an alarm when the cron stops |
SENTRY_DSN | error reports to your own Sentry project. Without it, fepli sends none. |
Tuning
| Variable | Default | What it does |
|---|---|---|
FRANKENPHP_MAX_THREADS | 6 | how many requests one web container handles at the same time. Each can take up to 512 MB of memory. |
PORT | 80 | the port the web container listens on |
MIGRATE_ON_START | 1 | whether the web container migrates the database when it starts. Set 0 to run app:migrate yourself. |
RELYING_PARTY_NAME | fepli | the name browsers show when an admin creates a passkey |
Set by the image
Leave APP_ENV=prod and APP_DEBUG=0 as the image sets them. Debug mode shows internals to every visitor.
The worker gets one variable more than the others: IS_WORKER=1.
A complete example
fepli.env
# openssl rand -hex 32
APP_SECRET=9f1c…
# openssl rand -base64 32, and never change it
INTEGRATIONS_ENCRYPTION_KEY=Qm3v…
DATABASE_URL=mysql://fepli:secret@mariadb:3306/fepli
DATABASE_SERVER_VERSION=mariadb-12.3.0
REDIS_URL=redis://redis:6379/1
LOCK_DSN=redis://redis:6379/2
SESSION_DSN=redis://redis:6379/3
MESSENGER_TRANSPORT_DSN=redis://redis:6379/messages
# With Varnish. Without it: VARNISH_HOST=
VARNISH_HOST=varnish:80
VARNISH_BASE_URL=http://varnish
# openssl rand -hex 32; Varnish gets the same value
VARNISH_PURGE_TOKEN=5b0e…
# Only where your proxies connect from outside Docker's Compose networks
#TRUSTED_PROXIES=10.0.0.0/8
TRUSTED_HOSTS='^(www\.)?example\.fepli\.eu$'
APP_BASE_URL=https://example.fepli.eu
MAILER_DSN=smtp://ferienpass%40example.fepli.eu:secret@smtp.example.fepli.eu:587
ADMIN_EMAIL=ferienpass@example.fepli.eu
# Only with object storage. Without it, uploads stay in the storage volume
#S3_FILES_BUCKET=fepli-files
#S3_EXPORTS_BUCKET=fepli-exports
#AWS_S3_ENDPOINT=https://s3.example.fepli.eu
#AWS_S3_REGION=eu-central
#AWS_S3_ACCESS_ID=…
#AWS_S3_ACCESS_SECRET=…
Docker Compose reads such a file with env_file:. Put regular expressions in single quotes, so that Compose leaves the $ and the backslashes in them alone. The file holds the secrets: make it readable only for yourself (chmod 600).