Getting started
Environment variables
Externa reads configuration from .env (copied from .env.example during composer setup). Most keys map into config/*.php via env(). This page groups the variables you will actually set for externa-core and documents defaults.
Note
After changing env values that are cached in production, run php artisan config:clear or rebuild config cache. Locally, composer test already clears config before Pest runs.
App
Standard Laravel application identity and runtime flags from .env.example:
| Variable | Default (example) | Purpose |
|---|---|---|
APP_NAME | Laravel | Application name (also used in mail from-name interpolation). Set to Externa if you want branding in mail/UI strings that read the app name. |
APP_ENV | local | Environment name (local, production, …). |
APP_KEY | (empty until generated) | Encryption key; composer setup runs key:generate. |
APP_DEBUG | true | Detailed errors when true — disable in production. |
APP_URL | http://localhost | Canonical URL (scheme + host + port). Must match how you browse. App (password, TOTP, CMS) works on HTTP; passkeys need a secure context (HTTPS, or http://localhost / *.localhost — not http://*.test). Drives Fortify RP ID / allowed_origins. See Passkeys — Requirements. |
APP_LOCALE | en | Default locale. |
APP_FALLBACK_LOCALE | en | Fallback locale. |
APP_FAKER_LOCALE | en_US | Faker locale for factories/seed demos. |
APP_MAINTENANCE_DRIVER | file | Maintenance mode driver. |
APP_MAINTENANCE_STORE | (commented) | Optional store when using database maintenance driver. |
BCRYPT_ROUNDS | 12 | Password hashing rounds. |
LOG_CHANNEL | stack | Default log channel. |
LOG_STACK | single | Channels in the stack. |
LOG_DEPRECATIONS_CHANNEL | null | Deprecation log target. |
LOG_LEVEL | debug | Minimum log level. |
VITE_APP_NAME | "${APP_NAME}" | Exposed to the Vite/React build as the app name. |
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
APP_LOCALE=en
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=en_US
APP_MAINTENANCE_DRIVER=file
BCRYPT_ROUNDS=12
LOG_CHANNEL=stack
LOG_STACK=single
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
VITE_APP_NAME="${APP_NAME}"
Database, session, cache, and queue
Externa’s .env.example defaults to SQLite, database sessions, and Redis for queue + cache (full stack). For light local without Redis, set QUEUE_CONNECTION=database and CACHE_STORE=database.
| Variable | Default | Purpose |
|---|---|---|
DB_CONNECTION | sqlite | Database driver. |
DB_HOST | 127.0.0.1 (commented) | Host for MySQL/Postgres. |
DB_PORT | 3306 (commented) | Port for non-SQLite drivers. |
DB_DATABASE | laravel (commented) | Database name / path depending on driver. |
DB_USERNAME | root (commented) | DB user. |
DB_PASSWORD | (empty, commented) | DB password. |
SESSION_DRIVER | database | Session storage. |
SESSION_LIFETIME | 120 | Minutes. |
SESSION_ENCRYPT | false | Encrypt session payload. |
SESSION_PATH | / | Cookie path. |
SESSION_DOMAIN | null | Cookie domain. |
QUEUE_CONNECTION | redis (fallback database) | Full stack uses Redis + Horizon. Minimal: database + queue:listen. |
CACHE_STORE | redis | Prefer Redis for app / public API cache; database is fine for light local. |
CACHE_PREFIX | (commented) | Optional key prefix. |
BROADCAST_CONNECTION | reverb (fallback log) | Full: Reverb. Minimal: log (60s notification poll only when realtime is off). |
FILESYSTEM_DISK | local | Default filesystem disk. |
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
# Full stack defaults in .env.example — switch to database/log for minimal:
BROADCAST_CONNECTION=reverb
FILESYSTEM_DISK=local
QUEUE_CONNECTION=redis
CACHE_STORE=redis
# CACHE_STORE=database # ok for light local without Redis cache
Redis (Herd / full stack)
Required for Horizon, recommended for app cache / public API performance, plus Reverb-friendly ops and Pulse redis ingest — see Redis prerequisite.
| Variable | Default |
|---|---|
REDIS_CLIENT | phpredis |
REDIS_HOST | 127.0.0.1 |
REDIS_PASSWORD | null |
REDIS_PORT | 6379 |
REDIS_CACHE_DB | 1 (app cache; keep off queue DB) |
REDIS_PULSE_DB | 2 (optional; connection pulse) |
MEMCACHED_HOST | 127.0.0.1 |
Reverb (Herd Pro defaults)
Match Herd → Services → Reverb (:8080). Host for Echo must be 127.0.0.1 or localhost (not 0.0.0.0). Restart Vite after changing VITE_REVERB_*.
| Variable | Default | Notes |
|---|---|---|
REVERB_APP_ID | 1001 | Herd shared app id |
REVERB_APP_KEY | laravel-herd | Shared with Vite |
REVERB_APP_SECRET | secret | Server secret |
REVERB_HOST | 127.0.0.1 | WS host for clients |
REVERB_PORT | 8080 | Herd Reverb port |
REVERB_SCHEME | http | http / https |
VITE_REVERB_APP_KEY | "${REVERB_APP_KEY}" | Echo key |
VITE_REVERB_HOST | "${REVERB_HOST}" | Echo host |
VITE_REVERB_PORT | "${REVERB_PORT}" | Echo port |
VITE_REVERB_SCHEME | "${REVERB_SCHEME}" | Echo TLS flag |
Standalone (no Herd Reverb): set your own REVERB_APP_* and run php artisan reverb:start.
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=1001
REVERB_APP_KEY=laravel-herd
REVERB_APP_SECRET=secret
REVERB_HOST=127.0.0.1
REVERB_PORT=8080
REVERB_SCHEME=http
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"
Pulse
| Variable | Default | Notes |
|---|---|---|
PULSE_ENABLED | true | Master switch |
PULSE_INGEST_DRIVER | redis | Or storage without Redis worker |
PULSE_REDIS_CONNECTION | pulse | Dedicated Redis connection |
PULSE_STORAGE_KEEP | 2 days | Trim window |
PULSE_INGEST_KEEP | 2 days | Ingest trim |
Warning
With Herd Reverb already on :8080, full local ops need horizon + pulse:work (do not also reverb:start). Standalone: composer run dev:full starts all three. Minimal: composer run dev + queue:listen. See Minimal vs full stack.
Local default is the log mailer (messages go to the log, not SMTP).
| Variable | Default | Purpose |
|---|---|---|
MAIL_MAILER | log | Mail transport. |
MAIL_SCHEME | null | Scheme when using SMTP-like transports. |
MAIL_HOST | 127.0.0.1 | SMTP host. |
MAIL_PORT | 2525 | SMTP port. |
MAIL_USERNAME | null | SMTP user. |
MAIL_PASSWORD | null | SMTP password. |
MAIL_FROM_ADDRESS | hello@example.com | From address. |
MAIL_FROM_NAME | "${APP_NAME}" | From display name. |
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS (optional)
Scaffolded for S3-compatible disks if you configure FILESYSTEM_DISK / filesystems config accordingly:
| Variable | Default |
|---|---|
AWS_ACCESS_KEY_ID | (empty) |
AWS_SECRET_ACCESS_KEY | (empty) |
AWS_DEFAULT_REGION | us-east-1 |
AWS_BUCKET | (empty) |
AWS_USE_PATH_STYLE_ENDPOINT | false |
Files
Mapped in config/files.php:
| Variable | Default | Purpose |
|---|---|---|
FILES_DUPLICATE_SYNC_MAX_BYTES | 52428800 (50 MB) | Single non-folder files at or below this size duplicate synchronously. Folders, bulk selections, and larger files use DuplicateFilesJob. |
FILES_ZIP_TTL_MINUTES | 60 | Prepared zip lifetime under storage/app/zips before cleanup (files:cleanup-zips / related command). |
FILES_ZIP_MAX_BYTES | 104857600 (100 MB) | Maximum zip payload size enforced by application config. |
FILE_PUBLIC_URL_BASE | (optional) | Override base URL for public/asset disks (config/filesystems.php). Also documented under Files & storage. |
FILES_DUPLICATE_SYNC_MAX_BYTES=52428800
FILES_ZIP_TTL_MINUTES=60
FILES_ZIP_MAX_BYTES=104857600
# FILE_PUBLIC_URL_BASE=
Note
Public URLs for the public disk still require php artisan storage:link regardless of these variables.
AI
Mapped primarily in config/ai.php. Externa defaults to a local OpenAI-compatible provider (LM Studio, vLLM, etc.).
| Variable | Default | Purpose |
|---|---|---|
AI_DEFAULT_PROVIDER | local | Provider key under config('ai.providers'). |
LOCAL_AI_URL | http://127.0.0.1:1234/v1 | Base URL for the local OpenAI-compatible API. |
LOCAL_AI_API_KEY | (empty) | Optional bearer key if your gateway requires one. |
LOCAL_AI_MODEL | openai/gpt-oss-20b in .env.example (local-model if unset in config) | Default text model for the local provider. Prefer tool-calling-capable models. |
AI_REMOTE_IMPORT_HOSTS | (empty) | Comma-separated host allowlist for remote JSON import tools. Empty means no remote hosts allowed. |
AI_WEBHOOK_TOKEN | (empty) | Shared secret for collection import webhook authentication. |
AI_DAILY_PROMPT_LIMIT | 0 | Max prompts per day; 0 disables the limit. |
AI_EMBEDDINGS_ENABLED | false | Toggles embeddings-related features. |
AI_MCP_ENABLED | false | Toggles MCP-related features. |
AI_DEFAULT_PROVIDER=local
LOCAL_AI_URL=http://127.0.0.1:1234/v1
LOCAL_AI_API_KEY=
LOCAL_AI_MODEL=openai/gpt-oss-20b
AI_REMOTE_IMPORT_HOSTS=
AI_WEBHOOK_TOKEN=
AI_DAILY_PROMPT_LIMIT=0
AI_EMBEDDINGS_ENABLED=false
AI_MCP_ENABLED=false
Other AI providers (optional)
config/ai.php also documents keys for cloud providers when you switch AI_DEFAULT_PROVIDER (or call a named provider explicitly). These are not all listed in .env.example, but are read when present:
| Variable | Used by |
|---|---|
ANTHROPIC_API_KEY, ANTHROPIC_URL | Anthropic |
AZURE_OPENAI_API_KEY, AZURE_OPENAI_URL, AZURE_OPENAI_API_VERSION, AZURE_OPENAI_DEPLOYMENT, embedding/image deployment vars | Azure OpenAI |
AWS_BEDROCK_REGION, AWS_BEARER_TOKEN_BEDROCK, AWS_SESSION_TOKEN, AWS_USE_DEFAULT_CREDENTIALS | Bedrock (plus standard AWS keys) |
COHERE_API_KEY | Cohere |
DEEPSEEK_API_KEY | DeepSeek |
ELEVENLABS_API_KEY | ElevenLabs |
GEMINI_API_KEY, GEMINI_URL | Gemini |
GROQ_API_KEY | Groq |
JINA_API_KEY | Jina |
MISTRAL_API_KEY | Mistral |
OLLAMA_API_KEY, OLLAMA_URL | Ollama (http://localhost:11434) |
OPENAI_API_KEY, OPENAI_URL | OpenAI |
OPENROUTER_API_KEY | OpenRouter |
VOYAGEAI_API_KEY | Voyage |
XAI_API_KEY | xAI |
The local provider uses the openai driver with a custom url — laravel/ai accepts that pattern for OpenAI-compatible gateways.
Warning
Never commit real AI_WEBHOOK_TOKEN, cloud API keys, or production LOCAL_AI_API_KEY values. Rotate webhook tokens if they leak; remote import without a tight AI_REMOTE_IMPORT_HOSTS allowlist is unsafe.
Seeding (initial super admin)
Read by config/super_admin.php and used only when CreateSuperAdminSeeder runs (via php artisan db:seed).
| Variable | Default | Purpose |
|---|---|---|
INITIAL_SUPER_ADMIN_FIRST_NAME | Super | First name. |
INITIAL_SUPER_ADMIN_LAST_NAME | Admin | Last name. |
INITIAL_SUPER_ADMIN_EMAIL | superadmin@example.com | Login email. |
INITIAL_SUPER_ADMIN_PASSWORD | password | Login password (local only). |
INITIAL_SUPER_ADMIN_FIRST_NAME=Super
INITIAL_SUPER_ADMIN_LAST_NAME=Admin
INITIAL_SUPER_ADMIN_EMAIL=superadmin@example.com
INITIAL_SUPER_ADMIN_PASSWORD=password
These keys may be omitted from .env.example because config defaults already match local development; set them explicitly before seeding shared environments.
Built-in roles created by seeders (not env vars): super-admin, admin, reader, public.
Public CMS API
| Variable | Default | Purpose |
|---|---|---|
API_KEY_RATE_LIMIT | 60 | Default requests/minute per API key when the key has no explicit limit (config/api.php). |
CORS_ALLOWED_ORIGINS | * | Comma-separated origins for api/* when project public_api_allowed_origins is empty (config/cors.php). Prefer the project allowlist in production; see Origin allowlist. |
LIGHTHOUSE_QUERY_CACHE_ENABLE | true | Cache parsed GraphQL query strings (config/lighthouse.php). |
LIGHTHOUSE_QUERY_CACHE_MODE | opcache | Prefer opcache (default) over store when CACHE_STORE=database — avoids serializing AST into the DB cache. |
API_KEY_RATE_LIMIT=60
CORS_ALLOWED_ORIGINS=*
Setup and auth model: Public CMS API.
Activity log
From config/activitylog.php (Spatie Laravel Activitylog):
| Variable | Default | Purpose |
|---|---|---|
ACTIVITYLOG_ENABLED | true | When false, activities are not persisted. |
ACTIVITYLOG_BUFFER_ENABLED | false | Buffer activities in memory and flush after the response — useful only for high-volume logging per request. |
ACTIVITYLOG_ENABLED=true
ACTIVITYLOG_BUFFER_ENABLED=false
Other activity log options (retention days, model class, soft-deleted subjects) are config-file defaults rather than .env keys — see config/activitylog.php (clean_after_days defaults to 365).
Quick reference: externa-specific block
Copy-paste block of the Externa-focused keys as they appear conceptually for a local AI-ready install:
# Files
FILES_DUPLICATE_SYNC_MAX_BYTES=52428800
FILES_ZIP_TTL_MINUTES=60
FILES_ZIP_MAX_BYTES=104857600
# FILE_PUBLIC_URL_BASE=
# AI (LM Studio / OpenAI-compatible local gateway)
AI_DEFAULT_PROVIDER=local
LOCAL_AI_URL=http://127.0.0.1:1234/v1
LOCAL_AI_API_KEY=
LOCAL_AI_MODEL=openai/gpt-oss-20b
AI_REMOTE_IMPORT_HOSTS=
AI_WEBHOOK_TOKEN=
AI_DAILY_PROMPT_LIMIT=0
AI_EMBEDDINGS_ENABLED=false
AI_MCP_ENABLED=false
# Seeding (optional overrides — config defaults apply if omitted)
INITIAL_SUPER_ADMIN_EMAIL=superadmin@example.com
INITIAL_SUPER_ADMIN_PASSWORD=password
# Activity log
ACTIVITYLOG_ENABLED=true
ACTIVITYLOG_BUFFER_ENABLED=false
# Public CMS API
API_KEY_RATE_LIMIT=60
CORS_ALLOWED_ORIGINS=*
Related docs
- Quick Start — Herd-oriented
.envand minimal commands - Installation — when
.envis created and seeded - Public CMS API —
/api/v1, keys, collection access - Files & storage — runtime file behavior
- AI configuration — providers and tool safety
- Application config — non-env config surfaces