Skip to content

Getting started

Installing

One install serves one organisation: a league, a club or a venue operator, with its website, admin, API and the apps' backend. With an Extended license, each of your clients gets an install of their own (the last section).

What you need

  • A domain for the platform, such as sports.example.com, pointing at the server.
  • A Linux server with Docker (the simple way), or one with PHP 8.4 or newer (8.5 recommended) and the extensions ctype, gd, iconv, intl, pdo_mysql and sodium, MySQL 8, Composer and Node 24 (only to build the website's styles).
  • A Mailgun sending domain, for the emails: sign-in codes, receipts, reminders. The platform runs without it, but nobody can reset a password.
  • The license key from your purchase email.

With Docker

On the server, in the backend's folder:

touch .env.local
docker compose -f compose.yaml build
docker compose -f compose.yaml run --rm --no-deps app php bin/new-instance --docker \
  --url=https://sports.example.com --brand="Harbour Sports" --from-email=noreply@sports.example.com
docker compose -f compose.yaml up -d

That starts three services:

ServiceWhat it does
appThe website, the admin and the API, behind Caddy, which gets the HTTPS certificate for the --url domain by itself (ports 80 and 443 must be open).
cronThe scheduled jobs: availability asks, reminders, account deletions, retention (Scheduled jobs and backups).
databaseMySQL 8.4, with its data in the db_data volume. Uploaded images are in the uploads volume.

Pushes and emails go out as the page that sends them loads. To send them from a background queue instead, set MESSENGER_TRANSPORT_DSN=doctrine://default in .env.local and start the worker service too: docker compose -f compose.yaml --profile worker up -d (Scheduled jobs and backups, Background queue).

Without Docker

composer install --no-dev --optimize-autoloader
npm ci && npm run build
mysql -u root -p -e "CREATE DATABASE sports CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  CREATE USER 'sports'@'127.0.0.1' IDENTIFIED BY 'PASSWORD'; GRANT ALL ON sports.* TO 'sports'@'127.0.0.1'"
php bin/new-instance --url=https://sports.example.com --brand="Harbour Sports" \
  --from-email=noreply@sports.example.com \
  --database-url="mysql://sports:PASSWORD@127.0.0.1:3306/sports?serverVersion=8.0.32&charset=utf8mb4"

Then point the web server at the public/ folder, with PHP-FPM, and let requests up to 13 MB through (an image upload is up to 10 MB). With Caddy, for example:

sports.example.com {
	root * /var/www/sports/public
	encode zstd gzip
	request_body {
		max_size 13MB
	}
	php_fastcgi unix//run/php/php-fpm.sock
	file_server
}

Add the scheduled jobs to the crontab (Scheduled jobs and backups, Crontab). Pushes and emails go out as the page that sends them loads; a background queue is optional (Scheduled jobs and backups, Background queue).

What bin/new-instance does

It gives this copy its own secrets in .env.local: the app secret, which also seals the secret settings until you give them a key of their own (Secret settings), the JWT passphrase and the public address; with Docker, the database's passwords and the domain for HTTPS. It makes the API's signing keys in config/jwt/ and the folder for uploads. It never touches an install that's already set up, and never changes keys: new keys would sign everybody out.

--scheme=harboursports sets the link scheme of your app, when it isn't the default one. It must match the scheme in your app's brand.json.

The installer

Open https://sports.example.com/install. Each step shows a green check once it's done:

  1. Server requirements: PHP's version and extensions, and the folders that must be writable. Your host enables what is missing.
  2. Database: already connected when bin/new-instance wrote the database address. Otherwise enter the connection, and the installer saves it.
  3. Prepare backend: makes the tables, the settings and the sport presets, from the browser, and the API's signing keys if they are missing.
  4. Platform settings: the platform's name, public address, currency, country, language and logos, and Mailgun if you have it now. Everything here can change later in the admin.
  5. Admin account: the first administrator.
  6. License: your key. It activates once, for this platform's domain. A Standard key runs one platform; an Extended key runs one for each of your clients.
  7. Finish: marks the platform installed, and closes the installer.

Then sign in at /admin and go through Configuring.

Before your first players arrive

  • Emails: Settings, Mail & notifications: Mailgun's key and domain, and "Send emails" on. Register a test player and check that the code arrives.
  • The apps' links: Settings, Website: the store links, and the identifiers that let invite links open the app (Apple team ID, bundle ID, Android package and its SHA-256 fingerprint). They are in your app's brand.json and its signing key.
  • Payments, when you take them: Payments.
  • Privacy: check the privacy policy and the terms, and Settings, Privacy (Privacy and GDPR).
  • Backups: a nightly copy, kept somewhere else than this server (Scheduled jobs and backups, Backups). Keep .env.local and config/jwt/ with them: without them, a restored database can't be read or signed into.
  • A check: php bin/console app:health (with Docker: docker compose -f compose.yaml exec app php bin/console app:health) checks the database, the migrations, the settings, the signing keys, the writable folders and the API.

New releases are taken with bin/upgrade (Upgrading). The admin's Platform updates page says when one is out.

Another client's platform

With an Extended license, each client gets their own install: their own copy of the backend (a folder, or a server), their own database, domain and bin/new-instance, and the same license key in step 6. Their app is a brand of its own (Your brand), packed with their name, colours and store accounts. Nothing is shared between two clients' platforms.

When something goes wrong

What you seeWhere to look
An error page, or the app can't sign inThe admin's Application logs, or var/log/prod.log; then app:health.
The installer says a folder isn't writableThe web server's user must own var/ and public/uploads/.
Images don't uploadThe web server's body size limit (13 MB above), and public/uploads/ writable.
Emails don't arriveSettings, Mail & notifications: "Send emails" on, and Mailgun's region matches your domain's.
Pushes don't arriveThe app's Expo project (Building and publishing the apps), and the Expo access token in Settings, Mail & notifications if that project has Enhanced Security.
"License" stays redThe key from the purchase email, and the server can reach sharkhex.com. A Standard key runs one platform at a time: to move it, reply to the purchase email.

Found a mistake? Tell us.