# Production deployment

This is a Laravel 12 application for PHP 8.3 or newer and a MySQL 8-compatible database. An uploadable ZIP is a portable application package, not a way to bypass your host’s PHP, database, DNS, document-root or queue requirements. Use a host that allows command-line PHP, HTTPS and either a supervised queue worker or per-minute cron.

## 1. Check the host

Enable these PHP extensions for both web PHP and command-line PHP: `pdo_mysql`, `mbstring`, `openssl`, `tokenizer`, `xml`, `ctype`, `json`, `curl`, `fileinfo` and `zip`. Some extensions, including JSON, are built into current PHP versions. Check `php -v` and `php -m`; the CLI must use the same supported PHP version as the website. The production archive includes Composer vendor dependencies and public assets. If dependencies are missing or the archive is rebuilt, install Composer and run `composer install --no-dev --prefer-dist --optimize-autoloader` in the application directory.

Create a dedicated MySQL database and database user. Permit that user to access only this application database. Arrange a TLS certificate and a verified SMTP account for a domain you control. Confirm outbound access to the SMTP provider’s required port.

## 2. Extract outside the public web root

Upload the ZIP through your hosting panel or SFTP and extract it to a private application directory, for example `/home/account/starmanage`. **Set the domain document root to `/home/account/starmanage/public`**, not the application directory. Only the contents of `public/` may be web-accessible. Never expose `.env`, `vendor/`, `storage/`, `database/`, the ZIP or backups through the website. Delete the uploaded ZIP after extraction.

On Apache, allow the packaged `public/.htaccess` rewrite rules. On Nginx, use the standard Laravel front-controller configuration with requests routed to `public/index.php`. A host that cannot select a safe document root needs a hosting-specific configuration or a different plan; do not move the entire application into `public_html` as a shortcut.

The web and worker users need write permission on `storage/` and `bootstrap/cache/`. Use ownership or a shared group appropriate to the host; `775` directories can be suitable with correct ownership. Keep other application files non-writable by the web user where possible. Do not use blanket `777` permissions. Keep `.env` readable only by the application/CLI account that needs it.

## 3. Configure the environment and retain the key

From the extracted application directory, on a **new installation only**:

```bash
cp .env.example .env
php artisan key:generate
```

Do not copy over an existing production `.env`. Set the following values, replacing every placeholder with your host’s actual values. The examples deliberately contain no working credentials:

```dotenv
APP_NAME="Star Management Group"
APP_ENV=production
APP_DEBUG=false
APP_URL=https://starmanagementgroup.online

DB_CONNECTION=mysql
DB_HOST=YOUR_DATABASE_HOST
DB_PORT=3306
DB_DATABASE=YOUR_DATABASE_NAME
DB_USERNAME=YOUR_DATABASE_USER
DB_PASSWORD="YOUR_DATABASE_PASSWORD"

SESSION_DRIVER=database
SESSION_ENCRYPT=true
SESSION_SECURE_COOKIE=true
SESSION_SAME_SITE=lax
QUEUE_CONNECTION=database
DB_QUEUE_RETRY_AFTER=120
STAR_MAIL_ENABLED=false

MAIL_MAILER=smtp
MAIL_SCHEME=smtp
MAIL_HOST=YOUR_SMTP_HOST
MAIL_PORT=587
MAIL_USERNAME=YOUR_SMTP_USERNAME
MAIL_PASSWORD="YOUR_SMTP_PASSWORD"
MAIL_FROM_ADDRESS=YOUR_VERIFIED_SENDER_EMAIL
MAIL_FROM_NAME="YOUR_BUSINESS_NAME"
```

Use `APP_URL` for the final public HTTPS domain, without a trailing slash. Do not leave a Preview address or `localhost` in production. You may configure SMTP in `.env`, or in the admin SMTP form. When no admin SMTP host is configured, the environment host, port, username and scheme are used. Admin SMTP credentials override environment values; an absent encrypted admin password falls back to `MAIL_PASSWORD`. Set a new password in the admin when changing providers. From email falls back to `MAIL_FROM_ADDRESS` when blank; customize the sender name in admin. Complete and test the chosen settings before sending. Use the SMTP provider’s actual security configuration: typically STARTTLS on port 587 or implicit TLS on port 465. For the environment transport, `MAIL_SCHEME=smtps` selects implicit TLS; do not disable certificate verification. Do not put passwords in documentation, screenshots or the distributed archive.

**Generate `APP_KEY` once. Never regenerate it after encrypted SMTP settings have been saved.** Losing or changing it makes those secrets unreadable and also affects encrypted cookies and other encrypted data. Store a secure backup of the production key together with protected environment/database backups. A restored installation must use its original key. Deployments and upgrades must preserve the existing `.env`.

For a standalone site, keep `SESSION_SAME_SITE=lax`. If the admin is deliberately embedded on a different site, use `SESSION_SAME_SITE=none` with `SESSION_SECURE_COOKIE=true` and HTTPS throughout. Browser third-party-cookie restrictions can still prevent embedded sign-in; standalone admin access is the reliable default. Do not weaken CSRF protection to compensate for embedding.

## 4. Create the database — choose one path

Use a new, empty database. **Import the packaged SQL schema or run migrations, not both for initial installation.** Back up any existing database before upgrade work.

**Option A — SQL import:** Import `database/sql/install.sql` with your hosting database tool or the MySQL client. This file is generated for the final distribution and contains the installation schema/default data; it is not a production-data backup.

```bash
mysql --host=YOUR_DATABASE_HOST --user=YOUR_DATABASE_USER --password YOUR_DATABASE_NAME < database/sql/install.sql
```

The command prompts for the database password. Do not put the password directly in a shell command. If the distribution does not contain `database/sql/install.sql`, use the migration path instead; do not import a guessed or incomplete schema.

**Option B — migrations:** Run the packaged migrations against the configured empty database:

```bash
php artisan migrate --force
```

Then fill any missing default template/settings records using the project’s safe seeder:

```bash
php artisan db:seed --force
```

Run this seeding command after **either** initial database setup path. The SQL export includes schema, migration history and default template rows but deliberately omits settings; seeding inserts those missing safe settings. The packaged seeder inserts defaults **only when absent** and preserves user edits. It is not a credential reset and must not create a default privileged account. Never substitute `migrate:fresh`, `db:wipe` or a destructive sample seeder on a live database. Future upgrades must follow their migration instructions; initial-import and upgrade workflows are different.

## 5. Create your administrator

Create the first admin using your own email and a strong, unique password:

```bash
php artisan star:admin admin@YOUR_DOMAIN
```

The command asks for the password interactively, rather than exposing it in process arguments or shell history. There is **no default admin password**. Use the real address of an administrator; the example is not a usable account. Visit `https://starmanagementgroup.online/admin` on the configured domain to sign in. The sign-in page is `/admin/login`. Change admin credentials only through the documented command or authenticated admin controls, and keep recovery access to the host.

In admin settings, replace the illustrative brand/contact placeholders. Set the business/sender name, verified sender email, public contact email and phone if appropriate. **No real physical business address is supplied by default. Enter a valid business mailing address before any campaign send.** Leave social links blank unless you have genuine Instagram, LinkedIn or Facebook URLs. Upload or configure authorized branding and images; do not imply celebrity representation.

Configure SMTP with the provider’s host, port, TLS mode, username, password and verified sender identity. Passwords are encrypted in the database using `APP_KEY`. A blank password field must not be treated as a recovered secret. Missing configuration must block campaign sending. Use an explicit test recipient that you control before selecting an opted-in campaign audience.

Sending starts disabled. After configuring your authorized SMTP account and production HTTPS URL, set `STAR_MAIL_ENABLED=true` in your private `.env` to permit controlled test messages and campaigns. Run `php artisan optimize:clear` after changing the environment, and restart workers. This switch is deliberately not enabled by saving the SMTP form.

## 6. Run the database queue

Campaign jobs need a worker; a completed browser request does not process the queue. Set `QUEUE_CONNECTION=database` and `DB_QUEUE_RETRY_AFTER=120`. The worker timeout must stay **shorter** than `retry_after`, with room for normal transport latency. The commands below use a 60-second timeout and at most three attempts. Enable the CLI `pcntl` extension where supported for Laravel worker timeout enforcement, and account separately for the host’s process-execution limits. Jobs may define their own more restrictive retry policy. Do not increase retries without investigating SMTP errors: a timeout after remote acceptance can produce a duplicate on retry, because SMTP is not an exactly-once protocol.

On a VPS or a host with process supervision, run this from the application directory under Supervisor, systemd or the host’s managed worker facility:

```bash
php artisan queue:work database --queue=campaigns --sleep=1 --tries=3 --timeout=60 --max-time=3600
```

Configure the supervisor to restart exited workers, write logs outside the public directory and start after reboot. The application’s queued sends should retain their throttling; queue workers do not replace provider rate limits. After code/config deployments, run `php artisan queue:restart` so long-running workers load the new release.

On shared hosting, a per-minute cron can drain available jobs instead. Replace the private path and PHP binary with the actual host paths. This example uses `flock` to prevent overlapping workers; ask the host for an equivalent lock if it is unavailable:

```cron
* * * * * cd /home/account/starmanage && /usr/bin/flock -n /home/account/starmanage/storage/queue-worker.lock /usr/bin/php artisan queue:work database --queue=campaigns --stop-when-empty --max-time=50 --tries=3 --timeout=60 >> /home/account/starmanage/storage/logs/queue-worker.log 2>&1
```

Run either a supervised worker or this cron as your chosen processing strategy. A cron worker stops when no jobs are currently available; delayed jobs wait for a later run. Shared-host execution limits may require smaller batches. Monitor `storage/logs/laravel.log`, the worker log and `php artisan queue:failed`. Fix failures before deliberately retrying selected jobs; do not blindly retry all failures or re-send accepted recipients. No provider bounce webhook is implemented; follow [the manual suppression procedure](Delivery.md#bounces-complaints-and-manual-suppression).

## 7. Clear stale caches and verify

After the correct environment is saved and the database is installed:

```bash
php artisan optimize:clear
php artisan config:cache
php artisan view:cache
php artisan queue:restart
```

Verify the public site, inquiry form, chat, authenticated admin, database persistence and a controlled SMTP test. Confirm the actual email’s HTML and plain-text parts, visible business address, CTA, unsubscribe URL and one-click headers. Test the unsubscribe route over HTTPS and verify that the contact is excluded from later marketing sends. Use public, absolute HTTPS logo/banner URLs; the views normalize relative paths against the configured site/app URL, but a mail client cannot access a local file or private Preview server.

Check that `.env` and private directories cannot be fetched from the web, `APP_DEBUG=false` is effective, and session cookies are secure. Check SPF, DKIM, DMARC and reputation using [the delivery guide](Delivery.md) before a live campaign. SMTP acceptance is not proof of delivery or inbox placement.

## Optional container/platform deployment

The included Docker container definition is an alternative deployment starting point; it is not a published production service. Configure the final HTTPS domain, production database and environment on the chosen platform. Supply a persistent `APP_KEY` through the platform’s protected secrets mechanism and retain it across redeploys. Never generate a new key every time a container starts. Persistent database/storage and backup arrangements remain the operator’s responsibility.

Container startup does **not** migrate the database, seed defaults, create an administrator or run a queue worker. Complete the one-time database setup, absent-only `php artisan db:seed --force` and interactive `php artisan star:admin` procedure in a controlled platform console/release task. Provision a separate supervised queue-worker process/service using the same image, database, environment and key, or an appropriate host cron strategy. Publishing, custom-domain connection, HTTPS and SMTP/DNS verification are separate from building the image.

## Backups and maintenance

Back up the database, the original production `.env`/`APP_KEY`, authorized uploaded assets and any needed private storage. Encrypt backups, restrict access and test restoring them outside production. Keep PHP/Laravel/vendor dependencies patched. Preserve consent and suppression records during restores and imports so unsubscribed or bounced contacts are not accidentally reactivated. Domain connection, DNS changes and public hosting activation are separate operations from downloading the application ZIP.
