Operations
Deployment
Deploy externa-core like a standard Laravel + Vite app, then ensure Externa-specific workers, scheduler jobs, and AI/file storage are wired. Local setup details remain in Installation.
Deploy checklist
1. Environment secrets
Copy .env.example → production env store and set at least:
| Variable | Production note |
|---|---|
APP_ENV | production |
APP_DEBUG | false |
APP_KEY | Unique; never reuse from another env |
APP_URL | Canonical URL users browse (prefer HTTPS; required for passkeys). No trailing-slash / proxy mismatch |
DB_* | Production database (see SQLite swap below) |
SESSION_DRIVER / CACHE_STORE / QUEUE_CONNECTION | Prefer database or Redis — not array/sync |
INITIAL_SUPER_ADMIN_* | Strong password before first seed |
AI_WEBHOOK_TOKEN | Long random secret if webhooks are used |
LOCAL_AI_* / cloud provider keys | Point at a reachable gateway; do not commit keys |
AI_REMOTE_IMPORT_HOSTS | Tight allowlist if remote import is enabled |
Real MAIL_* if verification / password reset emails matter |
Full catalog: Environment variables.
After changing env on a config-cached host:
php artisan config:cache
# or config:clear during debugging
2. Build & migrate
composer install --no-dev --optimize-autoloader
npm ci
npm run build
php artisan migrate --force
Use your platform’s release process (zero-downtime, maintenance mode, etc.) as appropriate.
3. Seed / sync permissions
Fresh database:
php artisan db:seed --force
Runs PermissionSeeder → RoleSeeder → CreateSuperAdminSeeder.
Existing database after permission enum changes:
php artisan permissions:sync
# optional cleanup:
php artisan permissions:sync --prune
Do not re-seed blindly in production if it would reset roles or recreate a known super-admin password unexpectedly. Prefer sync + controlled role admin UI.
4. Storage link
php artisan storage:link
Required for public / assets disk URLs (/storage/...). See Files configuration. Ensure storage/ and bootstrap/cache/ are writable by the app user.
5. Queue worker (+ optional Reverb / Pulse)
Prefer Redis + Horizon when available:
php artisan horizon
Or a plain worker:
php artisan queue:work --tries=3 --timeout=300
If using the full realtime/observability stack, also supervise:
- A Reverb (or compatible) WebSocket server with matching
BROADCAST_*/REVERB_*/ builtVITE_REVERB_* php artisan pulse:workwhenPULSE_INGEST_DRIVER=redis
Dashboard Health surfaces native Pulse/Horizon metrics (no primary Open Pulse/Horizon CTAs). Restart workers on every deploy. Details: Operations · Minimal vs full stack.
6. Scheduler crontab
* * * * * cd /path/to/externa-core && php artisan schedule:run >> /dev/null 2>&1
This runs:
activitylog:clean(daily)files:cleanup-uploads/files:cleanup-zips(hourly)ai:cleanup-attachments(daily)ai:run-sync-sources(every minute)
7. AI gateway
If the assistant is enabled in production:
- Set
AI_DEFAULT_PROVIDER(defaultlocal) and the matching provider keys/URL. - Ensure the app server can reach
LOCAL_AI_URL(or the cloud provider) with enough timeout for tool loops (AppAssistantuses 300s). Behind nginx/Herd, also raisefastcgi_read_timeout(often ≥ 600s) so long local-model reasoning does not drop the browser SSE. - Prefer tool-calling-capable models.
- Set
AI_WEBHOOK_TOKENif using collection import webhooks; keep CSRF exception only for that path.
See AI configuration.
8. APP_URL and proxies
- Match the public scheme/host (Herd, reverse proxy, CDN).
- Set trusted proxies if TLS terminates upstream.
- Optionally set
FILE_PUBLIC_URL_BASEwhen asset URLs should use a CDN origin while the app stays on another host. - The CMS (password, TOTP, APIs) can run on HTTP, but prefer HTTPS in production.
- Passkeys: require HTTPS in production;
APP_URLmust match Fortify WebAuthnrelying_party_id/allowed_origins. See Passkeys — Requirements.
9. Production database (leave SQLite)
.env.example defaults to DB_CONNECTION=sqlite for local ease. For production:
- Provision MySQL 8+ or PostgreSQL.
- Set
DB_CONNECTION,DB_HOST,DB_PORT,DB_DATABASE,DB_USERNAME,DB_PASSWORD. - Run migrations against the new database.
- Keep session/cache/queue tables on a durable store (database or Redis).
SQLite ceilings
SQLite is fine for demos and single-user local work. Concurrent writers, large activity logs, and multi-worker queues push you toward MySQL/Postgres quickly.
Smoke test after deploy
GET /up→ healthy.- Login as super admin (or your provisioned admin) — password and, if enabled, Sign in with passkey.
- Open dashboard, files, collections (permissions permitting).
- Trigger a small queued job (e.g. zip) and confirm the worker processes it.
- If AI is enabled:
GET /ai/statusand a short chat with a read-only tool. - Confirm scheduled cleanups appear in logs after the next hour/day.
Related
- Operations — ongoing worker/scheduler/observability
- Horizon · Reverb & Echo · Pulse & Health
- Configuration — app/Fortify/session defaults
- Passkeys · Security checklist
- Installation — local Herd /
composer setuppath