Local setup
Run a complete fepli instance on your own machine, with the same image as in production. Use it to try fepli, to train colleagues or to rehearse an update. Nothing leaves your machine: every e-mail ends up in a mail catcher.
What you need
- Docker Desktop, or Docker Engine with the Compose plugin, with about 4 GB of memory for its containers.
- The user name and token fepli gave you for the image registry.
- Chrome, Edge or Firefox. They send every
*.localhostname to your own machine, which this setup uses for the tenants' domains. In other browsers, add127.0.0.1 musterstadt.localhostto your hosts file.
1. Log in to the registry
Log in once
docker login ghcr.io --username <your user name>
Paste the token when Docker asks for the password. Docker remembers the login.
2. Describe the instance
Create a folder, e.g. fepli, with two files. The first describes the containers:
compose.yaml
name: fepli
x-fepli: &fepli
image: ghcr.io/ferienpass/app:general-latest
platform: linux/amd64
env_file: fepli.env
restart: unless-stopped
depends_on:
mariadb:
condition: service_healthy
redis:
condition: service_started
volumes:
- files:/app/files
- storage:/app/storage
- images:/app/contao-assets/images
- share:/app/public/share
- indexes:/app/var/indexes
- deferred-images:/app/var/deferred-images
services:
web:
<<: *fepli
ports:
- '8080:80'
# The port opens once the database is migrated; the worker and the cron wait for it.
healthcheck:
test: ['CMD', 'php', '-r', 'exit(@fsockopen("127.0.0.1", (int) (getenv("PORT") ?: 80)) ? 0 : 1);']
interval: 10s
start_period: 10m
worker:
<<: *fepli
environment:
IS_WORKER: '1'
depends_on:
web:
condition: service_healthy
cron:
<<: *fepli
command: ['while true; do php bin/console contao:cron; sleep 60; done']
depends_on:
web:
condition: service_healthy
mariadb:
image: mariadb:12.3
restart: unless-stopped
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
environment:
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: fepli
MARIADB_USER: fepli
MARIADB_PASSWORD: fepli
healthcheck:
test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
interval: 10s
retries: 10
volumes:
- db:/var/lib/mysql
redis:
image: redis:8
restart: unless-stopped
command: ['redis-server', '--appendonly', 'yes', '--maxmemory-policy', 'noeviction']
volumes:
- redis:/data
mailpit:
image: axllent/mailpit
ports:
- '8025:8025'
volumes:
db:
redis:
files:
storage:
images:
share:
indexes:
deferred-images:
This setup runs without Varnish, which is simplest on your own machine. Add it when you want to try it.
The web, worker and cron containers are the same image with the same settings. Only their job differs: the worker gets IS_WORKER=1, and the cron runs the scheduled jobs once a minute. The volumes keep the uploaded files and the search index when you recreate the containers. The web and the worker share them, because the worker writes the exports the web container hands out.
The second file holds the configuration:
fepli.env
APP_SECRET=local-only-change-me-in-production
INTEGRATIONS_ENCRYPTION_KEY=local-only-change-me-in-production
DATABASE_URL=mysql://fepli:fepli@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
# Empty: no Varnish
VARNISH_HOST=
TRUSTED_HOSTS='^(localhost|[a-z0-9-]+\.localhost)$'
APP_BASE_URL=http://localhost:8080
FERIENPASS_PLATFORM_HOST=localhost
MAILER_DSN=smtp://mailpit:1025
ADMIN_EMAIL=ferienpass@musterstadt.de
SENTRY_DSN=
This makes localhost the platform console and lets every *.localhost name be a tenant, so you can try both single- and multi-tenant setups.
3. Start it
docker compose up -d
The first start downloads the images, which takes a few minutes. Then the web container creates fepli's tables before it starts to answer, and the worker and the cron start once it does. It does the same after every update: it changes only what the new version needs. docker compose ps shows whether everything runs; docker compose logs -f web shows what the web container does.
4. Create a tenant and its admin
Create the tenant Musterstadt, reachable at musterstadt.localhost, and its first admin:
Create the tenant
docker compose exec web php bin/console ferienpass:tenant:create musterstadt "Ferienpass Musterstadt" \
--host=musterstadt.localhost
Create the admin
docker compose exec web php bin/console ferienpass:user:create anna.schmidt@musterstadt.de \
--tenant=musterstadt --firstname=Anna --lastname=Schmidt
Choose a password when the command asks for one. Then:
| Open | To |
|---|---|
| http://musterstadt.localhost:8080/admin | sign in as Anna and use the admin of Musterstadt |
| http://localhost:8025 | read every e-mail fepli sent |
Musterstadt has no website yet: give it a root page in the CMS.
5. Try the platform console
For the console, create a global admin and sign in at http://localhost:8080:
Create a global admin
docker compose exec web php bin/console ferienpass:user:create you@example.org --global-admin
There you see Musterstadt in the tenant list, can step into it and can create more tenants, e.g. one at beispielhausen.localhost.
Add Varnish
To see fepli as it runs with a cache, put Varnish in front of the web container. Add the service to compose.yaml, and move the port from web to it:
compose.yaml
services:
web:
<<: *fepli
# as above, but without ports: Varnish is the way in now
varnish:
image: ghcr.io/ferienpass/varnish:latest
restart: unless-stopped
environment:
VARNISH_STORAGE: malloc,256m
ports:
- '8080:80'
depends_on:
- web
In fepli.env, tell fepli where Varnish is:
fepli.env
VARNISH_HOST=varnish:80
VARNISH_BASE_URL=http://varnish
Then docker compose up -d. The addresses stay the same. Remove the service and empty VARNISH_HOST again to go back.
Stop, update and start over
| Command | What it does |
|---|---|
docker compose stop | stops everything and keeps the data |
docker compose pull && docker compose up -d | updates to the latest image. The web container migrates the database when it starts. |
docker compose down --volumes | deletes the containers and all data, to start over |
When something doesn't work
- A
400for a domain: it doesn't matchTRUSTED_HOSTS. - A
404for a domain: no tenant claims it. Check the domains withferienpass:tenant:list. With Varnish, if you opened the domain before a tenant claimed it, Varnish may still hold the404: clear it withdocker compose exec web php bin/console app:flush-varnish --all-tenants. - An error page right after the start: the web container is still creating the tables. Wait until
docker compose logs webshowsStarting server. If it stops instead, the log above says why. - No e-mail arrives in Mailpit: the worker sends them. Check that it runs with
docker compose logs worker. - It is very slow on a Mac with Apple Silicon: the image is built for x86 and runs through emulation. Switch on "Use Rosetta for x86/amd64 emulation" in Docker Desktop's settings.