Admin Workflows
This page covers the development workflows for the Laravel 13 admin app — running the local Docker stack, bootstrapping users, and working with ingestion.
Prerequisites
- Docker (for the full local stack)
- PHP 8.4+ and Composer (if running outside Docker)
- pnpm (for JS assets)
Starting the local stack
The admin app uses compose.production.yaml as the base with a compose.local.yaml overlay for local-only services.
Full Docker stack
cd apps/admin
docker compose -f compose.production.yaml -f compose.local.yaml up -dOr from the monorepo root:
pnpm run docker:admin:local:upThis starts the local admin stack, including FrankenPHP, PostgreSQL, Redis, Horizon, Traefik, and the supporting services defined by the compose files.
Local URL: https://localhost/admin
The local stack runs over HTTPS with a self-signed certificate. Accept the browser warning or add it to your trusted certs.
App process only
If PostgreSQL, Redis, and the rest of the stack are already running, you can start just the Laravel app and Vite watcher:
cd apps/admin
composer install
composer devThis path does not provision PostgreSQL, Redis, Horizon, or Traefik. In the checked-in local admin setup, this mode typically runs from http://localhost:8000.
Running migrations
Run migrations before seeding roles or bootstrapping users:
# Inside the container
docker compose -f compose.production.yaml -f compose.local.yaml exec -T app php artisan migrate
# Locally (without Docker)
cd apps/admin && php artisan migrateBootstrapping the first admin user
The Filament panel does not seed a default user. After migrations complete, bootstrap your first admin from the running container:
cd apps/admin
docker compose -f compose.production.yaml -f compose.local.yaml exec -T app php artisan db:seed --class=AdminRoleSeeder
docker compose -f compose.production.yaml -f compose.local.yaml exec -T app php artisan tinker --execute="\$user = \App\Models\User::updateOrCreate(['email' => '[email protected]'], ['name' => 'Kubuli Admin', 'password' => 'kubuli_admin_password']); \$user->syncRoles(['admin']);"Sign in at https://localhost/admin/login with [email protected] / kubuli_admin_password.
To test curator-only flows:
docker compose -f compose.production.yaml -f compose.local.yaml exec -T app php artisan tinker --execute="\$user = \App\Models\User::updateOrCreate(['email' => '[email protected]'], ['name' => 'Kubuli Curator', 'password' => 'kubuli_curator_password']); \$user->syncRoles(['curator']);"Queue workers (Horizon)
The Docker stack includes a dedicated Horizon container. If you are running only the Laravel app process locally, start Horizon separately:
php artisan horizonEnvironment variables
cp apps/admin/.env.example apps/admin/.env
php artisan key:generateKey variables for local development:
APP_URL=https://localhost
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=kubuli
DB_USERNAME=postgres
DB_PASSWORD=password
QUEUE_CONNECTION=redis
N8N_API_URL=http://n8n:25678
N8N_API_KEY= # Set from your local n8n instance before provisioning workflowsThe checked-in .env.example uses host-reachable values for local host-run commands. When you exec into the Dockerized app, the compose stack provides the container-side database and service networking automatically.
n8n in development
The local Docker stack exposes n8n at http://localhost:25678. If you are using the full Docker stack above, no extra compose profile is required.
→ Full ingestion reference: Ingestion & Review
Running admin tests
# From apps/admin
php artisan test
# With coverage
php artisan test --coverage