Skip to content

Earn 30% on every license you refer. Join the affiliate program

Technical reference for hosting.

Optional scheduling, custom servers, database permissions, backups, and email details for you or your hosting provider.

Oct 7, 2026

Use this page for custom hosting or a specific setup problem. You can share the relevant section with your hosting provider. For the standard setup, start with Prepare your hosting.

Web server and file permissions

The standard package uses Apache or LiteSpeed with .htaccess support. An nginx-only server needs equivalent rewrite rules set up by its administrator.

The installer needs write access to .env; the app needs write access to storage and bootstrap/cache. Keep application files owned by the deployment user and grant only the access the web server needs. Never use 777 permissions. Keep .env, databases, logs, and backups outside public access.

Required PHP extensions

The installer checks these for you. This list is a reference for your hosting provider if a check fails.

  • BCMath (ext-bcmath)
  • Ctype (ext-ctype)
  • cURL (ext-curl)
  • DOM (ext-dom)
  • Exif (ext-exif)
  • Fileinfo (ext-fileinfo)
  • Filter (ext-filter)
  • GD (ext-gd)
  • Hash (ext-hash)
  • Iconv (ext-iconv)
  • Intl (ext-intl)
  • JSON (ext-json)
  • Libxml (ext-libxml)
  • Mbstring (ext-mbstring)
  • OpenSSL (ext-openssl)
  • PCRE (ext-pcre)
  • PDO (ext-pdo)
  • PDO SQLite (ext-pdo_sqlite)
  • Session (ext-session)
  • Tokenizer (ext-tokenizer)
  • XML (ext-xml)
  • Zip (ext-zip)
  • Zlib (ext-zlib)

Database permissions

Installation and updates need schema and data access within the installation's own database. The monetary-pass migration also needs CREATE and DROP for tables, SELECT, INSERT, UPDATE, and TRIGGER. Its insert and update triggers use SIGNAL to reject invalid financial values. SIGNAL is a SQL statement, not a separate permission to grant. Ask hosting support to check trigger and binary-log restrictions for the supported MySQL or MariaDB engine you use.

During a real upgrade, before changing application tables, this migration creates a short-lived probe table, checks that both triggers reject invalid writes, then removes the probe and its triggers. A migrate --pretend dry run lists schema statements; it does not check permissions or trigger enforcement. If the check fails, fix the permissions named in the error and retry. If the error names a probe table that could not be removed, ask the database administrator to remove only that probe first.

An existing or partial monetary schema needs a different recovery path: stop, take a current backup, and ask support or your database administrator to inspect the migration record, schema and financial history. Do not delete monetary tables or restore a stale backup over later financial records. See monetary upgrade safety.

Optional scheduled tasks

Core loyalty features work without cron. Authenticated admin, partner, or staff activity triggers due fallback work. Member and guest visits do not trigger it. With no dashboard activity, this work can wait.

For predictable timing, configure the scheduler every minute when the host allows it. If the plan offers only a longer interval, use the shortest interval available. Your host can enter the task for you; use the command in Health center.

The traffic fallback covers core daily loyalty work and bounded delivery tasks. It does not replace every maintenance job: sign-in codes expire without cron, but their scheduled database cleanup needs cron. Health center also offers Run now for the tasks listed there. See its task list for the complete behavior.

If you choose to enable cron, check Health center after the task has had time to run. A missing scheduler heartbeat is a warning about scheduled execution, not a failure of core loyalty features.

Optional queue workers

Keep the default QUEUE_CONNECTION=sync for a simple installation. No separate worker is needed. Member email and Google Wallet updates run on the same server right after the page has been sent, so visitors and staff do not wait for them. Other jobs run during the request.

If email or large batches slow requests, a technical operator can choose a database or Redis queue and manage a worker. Monitor failed jobs and arrange worker restarts after reboot or deployment. See queue setup.

Website PHP and updater PHP

The dashboard updater starts a separate command-line PHP process. It can use a different PHP version from the website even on the same hosting account. You do not need a terminal command to compare them.

After installation, open Health Center. It reports the website and updater checks available to the application. If it reports different PHP versions, contact hosting support and ask them to align scheduled and updater PHP with PHP 8.4.1 or newer for this domain.

Automatic updater requirements

The dashboard updater runs in a separate background process. You do not need queue:work, Supervisor, Redis, or cron, and the default QUEUE_CONNECTION=sync is fine. What it does need:

  • A working PHP command-line binary.
  • Permission to start a background process: exec() and nohup on Linux or macOS, popen() and start /B on Windows. Many shared hosts disable these.
  • Writable storage directories and the cache_locks database table, both present in a standard install.
  • Enough CPU, disk I/O, memory, and temporary space for the rollback download, package extraction, and file replacement. Shared plans with strict disk limits can stall during this work. The background process asks for up to 512 MB of PHP memory where the host allows it. If your host enforces a lower limit, raise it in your hosting panel before updating.

When a host blocks background processes, the update stops within about ten seconds, before it replaces any file, and reports the failure. Your installation keeps running on the current version. Use Manual file replacement instead, or ask your host to allow background processes. Install from Package does not help here because it uses the same background process.

Reward Loyalty finds the PHP command-line binary on its own: it checks the binary serving your site, its command-line siblings, the system PATH, and the usual locations on cPanel, Plesk, CloudLinux, and LiteSpeed hosts, and verifies the version of every candidate before using one. When no PHP 8.4.1+ command-line binary turns up, the update stops with a message listing every path it tried. If detection cannot find the host's PHP binary, set its path in .env:

UPDATER_PHP_BINARY=/opt/alt/php84/usr/bin/php

The path is verified before use, so a wrong value falls back to normal detection instead of breaking updates.

Installation and normal operation do not depend on exec or automatic-update resources. Both Dashboard Update and Install from Package use the same background process. When the host cannot run it, use manual updates.

When the background process stops before it replaces any file, for example on a PHP memory limit, the run shows as failed with the reason and you can start the update again. A run stopped by the host in another way may stay marked in progress. A technical operator can use php artisan updater:release to clear it; the command refuses while its process is still alive. Diagnose the hosting limit before retrying.

Files preserved during updates

When you run a dashboard update, install from package, or restore a package rollback, the system replaces application files except for protected paths. The update overwrites any file or directory outside the protected list.

Core protected paths

The updater always protects these paths:

  • .env, your environment configuration
  • .htaccess, server configuration
  • storage/app, application storage files
  • storage/logs, log files
  • bootstrap/cache, cached framework files
  • public/files, user uploads and media
  • public/.htaccess, public directory server config
  • public/favicon.ico, your site icon
  • database/database.sqlite, SQLite database (if using SQLite)

The core protected paths may change with future updates. The definitive list is always in config/reward-loyalty.php under the protected_paths array.

Protected files and new features

Protection comes with a trade-off. Your customized files survive every update, which also means a new feature cannot add its own settings to them. When a release introduces configuration keys for a protected file, your copy keeps working without them and the feature stays off until you add the keys yourself. The changelog notes this per release with the exact lines to add.

config/plans.php used to be the main example of this trade-off: it was protected, so you had to add new plan keys (prepaid passes, member segments) to it by hand after an update. Since version 5.10.0 the file is no longer protected. Updates replace it with the shipped defaults, so new plan keys now arrive on their own and need no hand edit.

That change has one consequence for installs that customized the file itself, as those earlier upgrade notes suggested. Prices, currency, limits, feature flags, or any other value you wrote into config/plans.php do not survive the update to 5.10.0 or later; the first sign is often plan prices showing in US dollars again. Re-enter those values once in the admin Plans editor or as PLAN_<TIER>_<SETTING> environment values. Both layers apply on top of the file and survive every future update. You can also grant a single feature per partner under Partners → Permissions without touching plan configuration at all.

Protecting custom files

If you've added custom translations, branding, or other files you want to preserve across updates, add them to your .env file:

PROTECTED_TRANSLATIONS="de_DE,fr_FR"
PROTECTED_PATHS="custom/branding/,my-custom-file.php"

PROTECTED_TRANSLATIONS: A comma-separated list of translation locale folders to preserve. These are relative to the lang/ directory. For example, de_DE,fr_FR protects lang/de_DE/ and lang/fr_FR/.

Note: PROTECTED_TRANSLATIONS only preserves files during updates. It does not make a language active. APP_ACTIVE_LOCALES controls which languages are visible. See Languages & Translations.

Translations edited in Languages & translations need no protection: they live in the database and survive every update on their own. Only files you changed under lang/ need a PROTECTED_TRANSLATIONS entry. After each update, the app records which texts the release added, changed, or removed, and the translation editor lists them for review. If you deploy from git or replace files manually instead of using the updater, run php artisan languages:sync once after the deploy. The generated resources/lang-release/ directory always follows the release and cannot be protected.

PROTECTED_PATHS: A comma-separated list of additional files and directories to preserve. Use paths relative to the application root. Add a trailing slash for directories (e.g., custom/branding/) or omit it for files (e.g., my-custom-file.php).

⚠️ Important: If you've made any customizations outside of protected paths, automated updates will overwrite them. Always add your custom paths to .env before updating.

Note: When using Manual File Replacement, you manage protected paths yourself by backing up and restoring files.

Reverse proxies

If a load balancer or reverse proxy sits in front of the app, set TRUSTED_PROXIES in the application's .env to a comma-separated list of that proxy's IP addresses or CIDR ranges. Ask your host for the correct values. Leave it empty when the app receives visitors directly; do not trust every address with *.

Confirm that requests through the proxy resolve to the visitor's IP address and the public HTTPS scheme. Sign-in limits use that address, so a wrong setup can put all visitors in one shared limit. Keep APP_URL set to the public application address, including https://.

Private test copies and backups

A private test copy, also called staging, lets you try updates and restore backups without changing your live site. Use a separate HTTPS address and database. Your license allows up to three activated domains.

Protect the copy with your host's access controls and keep it out of search results. Before opening a copy of live data, change its app address and database connection, route email to a test mailbox, and disable outbound integrations and scheduled jobs until isolated. Never point it at the live database or media storage.

Back up the database, .env (including the original APP_KEY), public/files/, storage/app/, and custom files. For SQLite, include database/database.sqlite. Keep backups private. Restore them to the test copy and check sign-in, a staff checkout, and an image upload. Record who can restore the backup and where the required credentials live.

Use a current backup when recovering a live site. Do not replace its database with an older test copy that omits later customer activity. The updater's file rollback is not a database backup. See manual update precautions.

PHP caching after file changes

OPcache can keep old PHP code in memory after installation or an update. If changes do not appear or the host requires it, use the host's supported PHP restart or OPcache reset. Then reopen the app and repeat the sign-in, checkout, and image checks. Do not add a public script that resets OPcache.

Checking an update method

Test an update on a private copy only when a safe, applicable package exists. Back up first, run the update, follow the host's PHP cache procedure, inspect Health center, and repeat the app checks. Until you have tested it, do not treat automatic-update compatibility as confirmed. Manual updates remain the fallback for restricted hosts.

Email provider settings

Reward Loyalty uses Laravel's mail system. The shipped configuration supports these production transport paths:

Path Reward Loyalty mailer When to use it
Standards-based provider SMTP smtp Use credentials issued for transactional sending. Brevo and SMTP2GO work through this path.
Mailgun API transport mailgun Use Mailgun credentials and the correct regional endpoint.
Amazon SES API transport ses Use a verified SES identity and production-ready AWS credentials.
Postmark API transport postmark Use a Postmark server token and authenticated sender.
Resend API transport resend Use a Resend API key and verified sending domain.

The configuration also contains local and diagnostic mailers such as log, array, and Mailpit. They do not deliver a production OTP to a real inbox. sendmail depends on a working local mail transfer agent and is not the default self-hosted recommendation.

Email authentication

Use a dedicated transactional sending domain or subdomain, such as notify.example.com. Keep the visible From address recognizable to members, and use a monitored Reply-To address when replies should reach a person.

Separating transactional identity from bulk marketing makes ownership and reputation easier to inspect. It does not remove the need to authenticate every service that sends for the parent domain.

Before adding DNS records, inventory existing senders. Then configure:

  • SPF, or the provider's aligned return-path record, so receiving systems can identify authorized infrastructure;
  • DKIM, so the provider signs mail with a key published in your DNS;
  • DMARC, so the domain owner sets alignment policy and receives reports.

Publish only one SPF policy for a hostname. Follow the selected provider's exact records instead of combining examples from different services. Start DMARC monitoring with a policy and reporting plan that fits your existing mail estate, inspect the reports, then tighten enforcement when every legitimate sender aligns.

Email settings in server configuration

Set the mailer and provider-issued values in .env:

MAIL_MAILER=smtp
MAIL_HOST=provider-smtp-host
MAIL_PORT=587
MAIL_USERNAME=provider-issued-username
MAIL_PASSWORD=provider-issued-secret
MAIL_ENCRYPTION=tls
[email protected]
MAIL_FROM_NAME="Your loyalty program"
MAIL_TIMEOUT=10

Use the port, encryption mode, username, and secret shown by the provider. Do not paste a normal mailbox password when the provider issues a separate SMTP credential. Store production credentials in the host's protected configuration and never in source control.

MAIL_TIMEOUT is how many seconds an SMTP connection or read may wait before it gives up. The default is 10. Without it, PHP's own socket default (often 60 seconds) applies, so an unreachable mail server could hold a request for a minute. Raise it only if a slow but working provider fails at 10 seconds.

Brevo through SMTP

Brevo documents its SMTP relay integration, including provider-issued SMTP credentials, in its SMTP integration guide. Use the SMTP key where Brevo requires it, not an unrelated API key. Configure the exact hostname, port, and encryption combination shown in the current Brevo account.

Add and authenticate the sending domain using Brevo's domain authentication and verification guide. Confirm the domain shows authenticated after DNS propagation before testing Reward Loyalty.

SMTP2GO through SMTP

SMTP2GO publishes its current server, ports, and authentication path in SMTP Settings. Create a dedicated SMTP user for Reward Loyalty and use the provider's supported encrypted port.

Verify a sender domain. SMTP2GO explains how its records cover SPF and DKIM, while DMARC remains a domain-level TXT record, in SPF, DKIM and DMARC Overview.

Email API connections

When using Mailgun, SES, Postmark, or Resend, choose the matching MAIL_MAILER and set the credentials required by the shipped config/services.php, config/mail.php, and provider package.

Do not copy credentials between providers or assume an SMTP password is also an API token. Use the provider's first-party setup and sender-authentication documentation for the selected transport.

Email delivery problems

Check delivery activity in your email provider when messages fail to arrive.

  1. Review provider activity for delivered, deferred, bounced, rejected, and complained messages.
  2. Stop sending to an address after a hard bounce unless you correct the address and the provider permits a retry.
  3. Keep complaint addresses suppressed unless the recipient confirms the complaint was accidental.
  4. Investigate spikes in soft bounces, authentication failures, or deferrals.
  5. Monitor the Reply-To inbox and DMARC reports.

Brevo exposes transactional activity for delivery review in its transactional activity API documentation. SMTP2GO documents bounces and rejections and spam complaint handling.

Reward Loyalty cannot repair a provider suppression or a receiving mailbox policy from the admin dashboard. Keep provider access and alert ownership with the person responsible for production email.

For a delivery investigation, inspect a real sign-in email's headers and provider activity for the expected SPF, DKIM, and DMARC results. Try a second mailbox provider when practical. The main setup check remains a code that arrives and completes sign-in.