Operations
Deploy with Docker
Operator path for self-hosted Externa: pull a published image (or build once), set a small .env, docker compose up, healthy stack. Mirrors the Directus-style friction goal.
Related
Deployment (bare metal + full Docker notes) · Installation — Docker local · Reverse proxy · Environment variables
Quick deploy (pull GHCR)
Published multi-arch image: ghcr.io/qiick-io/externa-core (linux/amd64 + linux/arm64). CI pushes on version tags (v*) and optional workflow dispatch.
# From an externa-core checkout (Compose + env sample live in the repo)
cp .env.docker.quick.example .env
# Required: APP_KEY, strong DB_PASSWORD, APP_URL
php -r "copy('.env', '.env.bak'); file_put_contents('.env', preg_replace('/^APP_KEY=.*/m', 'APP_KEY=base64:'.base64_encode(random_bytes(32)), file_get_contents('.env')));"
# First boot only — create roles + super admin (then set RUN_SEED=false)
# RUN_SEED=true
# INITIAL_SUPER_ADMIN_EMAIL=you@example.com
# INITIAL_SUPER_ADMIN_PASSWORD='…strong…'
docker compose -f compose.quick.yaml up -d
Open http://localhost:8080 (host APP_PORT, default 8080→80). Reverb publishes on host 8081.
| Piece | Default |
|---|---|
| Image | ghcr.io/qiick-io/externa-core:latest (EXTERNA_IMAGE override) |
| Services | app, horizon, reverb, scheduler, pulse, pgsql, redis |
| Volumes | externa_quick_storage, externa_quick_pgsql, externa_quick_redis |
| Healthcheck | GET /health/ready on app (start_period: 90s) |
Pin a release:
EXTERNA_IMAGE=ghcr.io/qiick-io/externa-core:1.0.0-beta.4 \
docker compose -f compose.quick.yaml up -d
Local smoke without GHCR pull (bake once, then quick Compose):
docker build --target production -t externa:prod .
EXTERNA_IMAGE=externa:prod docker compose -f compose.quick.yaml up -d
Minimal .env
| Variable | Required | Notes |
|---|---|---|
APP_KEY | yes | base64:… from key:generate / random 32 bytes |
APP_URL | yes | Public URL browsers hit (match proxy / port) |
DB_PASSWORD | yes | Shared with Compose pgsql |
DB_* | defaults | pgsql / externa / host pgsql inside Compose |
INITIAL_SUPER_ADMIN_* | when seeding | Email + strong password before RUN_SEED=true |
RUN_MIGRATIONS | sample true | Entrypoint runs migrate --force only when true |
RUN_SEED | sample false | Opt-in db:seed --force (roles + super admin) |
REVERB_* | sample | Change app key/secret for anything beyond laptop demos |
TRUSTED_PROXIES | behind TLS proxy | * or CIDRs — see Reverse proxy |
Full catalog: Environment variables — Docker Compose knobs.
First boot (gated)
Entrypoint defaults RUN_MIGRATIONS=false and RUN_SEED=false — restarts do not surprise-mutate the database unless Compose/env opts in.
- First bring-up: keep
RUN_MIGRATIONS=true(quick/prod samples). - Optional admin bootstrap: set strong
INITIAL_SUPER_ADMIN_*, thenRUN_SEED=truefor one start. - After login works: set
RUN_SEED=false(and optionallyRUN_MIGRATIONS=falseonce schema is stable). - Seeders are idempotent on permissions/roles/super-admin email — still treat seed-on-boot as a bootstrap flag, not a forever default.
No automatic destructive wipe. migrate --force only applies pending migrations.
Healthchecks
Compose app probes GET /health/ready (DB + required Redis). Also available:
| Endpoint | Role |
|---|---|
GET /up | Laravel liveness |
GET /health/live | Process up |
GET /health/ready | Ready for traffic (503 when checks fail) |
Details: Deployment — Health probes.
Publish / multi-arch path
# Bake amd64+arm64 (no push)
docker buildx bake -f docker-bake.hcl production
# or
./scripts/docker-buildx.sh production
# Push to GHCR (needs docker login ghcr.io)
VERSION=1.0.0-beta.4 PUSH=1 ./scripts/docker-buildx.sh production
CI: workflow docker — compose-config (includes compose.quick.yaml), single-arch image-build, multiarch-bake on PR/push, ghcr-publish on v* tags (+ workflow_dispatch).
Build-from-source production
When you need to bake assets yourself (custom Vite Reverb public URL, patches):
cp .env.docker.prod.example .env
# APP_KEY, secrets, APP_URL, TRUSTED_PROXIES, browser-facing REVERB_*
docker compose -f compose.prod.yaml up --build -d
Same healthchecks and gated RUN_MIGRATIONS / RUN_SEED as quick deploy.
Follow-through checklist
| Path | When | Link |
|---|---|---|
| Managed DB / Redis | Postgres/Redis outside Compose (RDS, Upstash, …) | compose.prod.yaml + compose.prod.managed.yaml — Deployment — Managed · core #15 |
| Reverse proxy / TLS | Public HTTPS + WebSocket Upgrade to Reverb | Reverse proxy cookbook · core #17 |
| Local full stack | Dev with Vite/Mailpit/MinIO | Installation — Docker |
| Bare metal | No Compose | Deployment |
Smoke after up
curl -fsS http://localhost:8080/health/ready→"status":"ok".- Login with
INITIAL_SUPER_ADMIN_*(if seeded) or your provisioned admin. - Confirm Horizon/scheduler containers are up; trigger a small queued job if useful.
Related
- Core README Docker sections · samples:
.env.docker.quick.example,.env.docker.prod.example,.env.docker.prod.managed.example - Upgrade · Backup & restore · Operations