Production › Deployment

Deployment

One image per Laravel app (infrastructure/docker/laravel.Dockerfile, PHP 8.4-FPM + nginx + ext-rdkafka + ext-redis). The same image runs web, queue workers, scheduler, the outbox publisher and Kafka consumers — only the command differs. Services deploy, scale and roll back independently.

#Lifecycle

StepCommand
BuildIMAGE_TAG=1.4.0 REGISTRY=ghcr.io/acme npx nestlaravel build (or docker build -f infrastructure/docker/laravel.Dockerfile --build-arg APP_DIR=apps/orders-service -t ghcr.io/acme/orders-service:1.4.0 .)
Testnpx nestlaravel test --affected (CI)
Packagedocker push ghcr.io/acme/orders-service:1.4.0
Deployupdate the tag for that service only (below)
Migratedocker compose run --rm orders-service php artisan migrate --force (run once per release, before/with rollout; keep migrations backward-compatible: expand → deploy → contract)
Scalemore replicas of web / queue:work / kafka:consume (≤ topic partitions per consumer group); one messaging:outbox-publish --daemon per service
Monitor/up (liveness) · /ready (DB) on services · /health/live, /health/ready, /health on the gateway; JSON logs with correlation_id; Kafka consumer lag & DLQ depth
Rollbackredeploy the previous image tag (images are immutable); events already published stay valid (schema versioning)

#Docker Compose on a single host / VPS

cp .env.example .env                 # or use the one `create` generated; set DB_PASSWORD, REDIS_PASSWORD, *_SERVICE_SECRET
docker compose up -d --build
docker compose exec app php artisan migrate --force
docker compose exec app php artisan db:seed --class='Database\Seeders\RolePermissionSeeder' --force

Only the gateway publishes a port (127.0.0.1:8000); put nginx/Caddy/Traefik in front for TLS. Keep the bundled Kafka for development: for production use a managed or 3-broker KRaft cluster with TLS+SASL (see KAFKA.md) and set KAFKA_BROKERS, KAFKA_SECURITY_PROTOCOL=sasl_ssl, KAFKA_SASL_*, KAFKA_SSL_CA_LOCATION.

#Linux VPS without containers

Install PHP 8.4 (+ rdkafka redis pdo_pgsql intl bcmath), nginx, supervisor. Per app: composer install --no-dev -o, php artisan config:cache route:cache, nginx root …/public, supervisor programs:

[program:orders-worker]   command=php artisan queue:work redis --tries=3 --max-time=3600
[program:orders-outbox]   command=php artisan messaging:outbox-publish --daemon
[program:orders-consumer] command=php artisan kafka:consume orders.events "App\Modules\Orders\Infrastructure\Messaging\Handler"

Bind services to a private interface and firewall them to the gateway host: ufw allow from <gateway-ip> to any port 8001.

#Kubernetes (optional)

Not required. If you use it: one Deployment + Service (ClusterIP) per app, NetworkPolicy allowing only the gateway → service; secrets from Secret/external-secrets; readinessProbe: /ready, livenessProbe: /up; a Deployment (replicas: 1) for the outbox publisher and one per consumer group; HPA on CPU or consumer lag (KEDA). Ingress → gateway only. Run migrations as a Job (Helm pre-upgrade hook).

#CI/CD

- run: npx nx show projects --affected -t build --json > affected.json
- run: IMAGE_TAG=${{ github.sha }} REGISTRY=ghcr.io/${{ github.repository_owner }} npx nestlaravel build --affected

#Publishing NestLaravel itself

Automated by .github/workflows/release.yml (tests → audit → build → clean-install test → package validation → version → tag → GitHub release → npm publish --provenance). Manual equivalent:

cd packages/cli
npm run build:templates && npm run verify:package     # secret scan + tarball allowlist
npm version 1.0.0 --no-git-tag-version                 # or minor/patch; update CHANGELOG.md first
npm pack                                               # inspect the tarball
npm login                                              # once (2FA)
npm publish --access public --provenance               # provenance needs GitHub Actions OIDC; drop it locally
git tag v1.0.0 && git push --tags && gh release create v1.0.0 --notes-file ../../CHANGELOG.md

Requirements: an npm account that owns the nestlaravel name (currently unclaimed on npm), an NPM_TOKEN (automation token) repository secret, and the repository.url in packages/cli/package.json pointing at your GitHub repo.

Edit this page on GitHub