Deployment and Operations
Recommended deployment line
The long-term deployment topology should continue to converge around:
Nginx
-> Laravel API
-> React SPA static assets
MariaDB
Redis
Analysis Worker
Docker Compose
Frontend transition note
The repository previously contained two frontend lines during the Nuxt-to-SPA transition. As of v1.4.3, the React/Vite SPA is the canonical frontend/ directory and the older Nuxt/Vue implementation has been removed from the active runtime tree.
The historical v1.1 transition work is retained in the roadmap documentation, but deployment operations should now use the normalized v1.4.3 layout described below.
Laravel runtime requirements
- Laravel remains the unified backend entry point
- database initialization uses migrations and seeders
- the old lightweight PHP implementation remains only as historical reference in
backend/legacy-lightweight/
Database initialization
After starting services, the recommended command is:
docker compose exec php php /var/www/html/artisan migrate --seed --force
To reset the database:
docker compose exec php php /var/www/html/artisan migrate:fresh --seed --force
Analysis Worker and Redis boundary
Current operating assumptions:
analysis_jobsare persisted in MariaDB- Redis list
ANALYSIS_JOB_REDIS_QUEUEis the async worker handoff boundary analysis-workerprocesses analysis workloads- the default YOLO model path is
analysis-worker/models/uprc2018/best.pt
Operationally, this should be understood as:
- Laravel creates and queries jobs
- Laravel pushes queued job IDs to Redis after durable database creation
- Analysis Worker consumes Redis, executes supported jobs, and reports results back through Laravel APIs
The default queue name is:
REDIS_PREFIX=
ANALYSIS_JOB_REDIS_QUEUE=ocean:analysis-jobs:queued
REDIS_PREFIX should remain empty for this worker handoff path so Laravel and the analysis worker read and write the same Redis list name.
If Redis is temporarily unavailable during job creation, the durable database row is still preserved. Operators can retry failed jobs or requeue jobs once Redis is healthy.
Common validation commands
Governance actor context
curl -s -X POST http://127.0.0.1:8080/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"password"}'
curl -s -H 'X-Ocean-Actor-Id: 3' http://127.0.0.1:8080/api/governance/me
curl -s http://127.0.0.1:8080/api/governance/roles
For v1.4.0, the SPA authenticates with POST /api/auth/login and sends Authorization: Bearer <token> for protected mutation routes. X-Ocean-Actor-Id is an internal identity-injection bridge, not a public authentication mechanism. It remains available for non-SPA tooling during the transition, but protected user mutation routes require bearer tokens.
Analysis Worker callbacks use an internal bridge header while the project waits for a real worker credential:
curl -s -H 'X-Ocean-Worker: ocean-analysis-worker' http://127.0.0.1:8080/api/analysis-jobs
Audit events
curl -s http://127.0.0.1:8080/api/audit-events?page_size=20
curl -s 'http://127.0.0.1:8080/api/audit-events?resource_type=analysis_job'
Use audit events to verify high-value actions such as task start/submit, sample result creation, exception resolution, and analysis job lifecycle transitions.
Settings and user management
After logging in as admin, capture the bearer token and validate the v1.4.1 governance pages through the API:
TOKEN="$(
curl -s -X POST http://127.0.0.1:8080/api/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"password"}' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["data"]["token"])'
)"
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/profile
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/settings
curl -s -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:8080/api/users?page_size=20'
curl -s -X PATCH http://127.0.0.1:8080/api/settings \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"language":"zh-Hans","display_density":"comfortable","default_workspace_tab":"settings"}'
To verify audit coverage for user/profile/settings changes:
curl -s 'http://127.0.0.1:8080/api/audit-events?resource_type=user&page_size=20'
Dashboard summary
curl -s http://127.0.0.1:8080/api/dashboard/summary
Inspection tasks
curl -s http://127.0.0.1:8080/api/inspection-tasks
Laravel API route list
docker exec ocean-php php /var/www/html/artisan route:list --path=api
Migration status
docker exec ocean-php php /var/www/html/artisan migrate:status
Analysis queue depth
docker exec ocean-redis redis-cli LLEN ocean:analysis-jobs:queued
Documentation site deployment
The documentation site lives in website/, uses Docusaurus, and is deployed independently to GitHub Pages.
The delivery rules are:
- documentation build stays separate from business service builds
- documentation can be published independently
- English is the default locale and Simplified Chinese remains available through i18n
Default SPA static hosting
The default Compose path now serves the React/Vite workspace frontend:
frontendbuilds fromdocker/frontend/Dockerfile- the SPA image serves static assets on port
80with history fallback toindex.html - top-level Nginx proxies
/to the SPA container - top-level Nginx routes
/api/to the Laravel / PHP entry point
The relevant files are:
docker/nginx/default.confdocker/frontend/Dockerfiledocker/frontend/nginx.confdocker/compose.local.yml- root
docker-compose.ymlcompatibility include
The earlier Nuxt implementation has been removed from the active repository layout. Historical context remains in the docs and git history.
The default local Compose service for asynchronous analysis is named analysis-worker, and its source directory is also analysis-worker/.
v1.4.2 production image direction
The next deployment hardening step is to package the main web application as one production image while keeping infrastructure and analysis workloads isolated.
Recommended production topology:
app
- Nginx
- PHP-FPM
- Laravel API
- built React/Vite SPA
db
redis
analysis-worker
The intended app image should replace the current production need for separate frontend, nginx, and php containers. It should not include MariaDB, Redis, or the analysis worker process.
The target image should be built with a multi-stage flow:
- build
frontend/withpnpm install --frozen-lockfileandpnpm run build - install backend Composer dependencies with
--no-dev --optimize-autoloader - copy Laravel source, vendor dependencies, and SPA build output into a runtime image
- run Nginx and PHP-FPM together through an entrypoint or supervisor
Production Nginx should serve SPA files directly with history fallback and route /api/ to Laravel public/index.php through local PHP-FPM. Runtime configuration must come from environment variables; do not bake .env secrets into the image.
The production path uses analysis-worker as both service and source directory name, because the service role is asynchronous analysis execution, image/model inference, and result write-back. Python remains the implementation language, not the deployment-facing product role.
Storage needs special handling: Laravel public/uploads storage should remain persistent and, when image analysis jobs require local files, should be mounted into analysis-worker with the same semantics currently used by OCEAN_STORAGE_ROOT.
Migrations should remain an explicit deployment step, for example:
docker compose -f docker/compose.prod.yml run --rm app php artisan migrate --force
Do not silently run migrations on every web container boot unless the release process explicitly adopts that policy. The app image supports an explicit opt-in switch, OCEAN_RUN_MIGRATIONS=true, for controlled environments that intentionally want startup migrations.
v1.4.3 repository layout direction
v1.4.3 normalized file locations without changing the product runtime responsibilities introduced in v1.4.2.
The active layout is:
backend/ Laravel API
frontend/ active React/Vite SPA
analysis-worker/ async analysis worker implemented in Python
docker/ Compose, Nginx, app image, and worker image assets
website/ Docusaurus documentation site
The old Nuxt/Vue implementation no longer occupies the active frontend/ path. Historical context is kept in documentation and git history rather than as a full runnable app. The obsolete SPA Compose example has been removed.
Root-level Compose files are minimized. Use explicit Docker paths such as docker/compose.local.yml and docker/compose.prod.yml; the root docker-compose.yml remains only as a lightweight compatibility include for local onboarding.
v1.4.4 release image publishing
Release tags now publish the deployable image pair for the production topology:
app: Nginx + PHP-FPM + Laravel API + built React/Vite SPAanalysis-worker: asynchronous analysis, image/model inference, and result write-back runtime
MariaDB and Redis remain separate stateful services and are not included in the release images.
GitHub releases publish to GitHub Container Registry:
ghcr.io/<owner>/<repo>/app:<release-tag>
ghcr.io/<owner>/<repo>/app:latest
ghcr.io/<owner>/<repo>/analysis-worker:<release-tag>
ghcr.io/<owner>/<repo>/analysis-worker:latest
CNB tag releases publish the same image pair to the CNB Docker registry under the repository slug:
$CNB_DOCKER_REGISTRY/$CNB_REPO_SLUG/app:<release-tag>
$CNB_DOCKER_REGISTRY/$CNB_REPO_SLUG/app:latest
$CNB_DOCKER_REGISTRY/$CNB_REPO_SLUG/analysis-worker:<release-tag>
$CNB_DOCKER_REGISTRY/$CNB_REPO_SLUG/analysis-worker:latest
Use immutable release tags for production rollouts. Treat latest as a convenience pointer for smoke testing or controlled environments that intentionally follow the newest published release.
The backend development Docker configuration path is docker/backend/. The local Compose service can still be named php for compatibility with existing commands, but new file-path references should use docker/backend/ rather than the old docker/php/ name.
Long-term direction explicitly not recommended
The project should not continue to treat Nuxt SSR / Nitro as the long-term deployment mainline, because:
- it offers limited value for an internal management workspace
- a long-running Node runtime increases deployment and troubleshooting complexity
- the separation of docs, workspace UI, and API is cleaner with SPA + Laravel