A repeatable deployment, in the order that avoids downtime and half-applied state.
- 1
Back up first
Take a database dump before any migration. Restoring is only possible if you did this.
docker compose -f docker-compose.prod.yml exec postgres pg_dump -U $DB_USERNAME $DB_DATABASE | gzip > backup-$(date +%F).sql.gz - 2
Fetch the release
git pull --ff-only - 3
Rebuild images
Rebuild nginx as well if anything under backend/public changed.
docker compose -f docker-compose.prod.yml build app - 4
Recreate containers
docker compose -f docker-compose.prod.yml up -d --no-deps app queue-worker scheduler - 5
Migrate
docker compose -f docker-compose.prod.yml exec app php artisan migrate --force - 6
Warm caches
Cached config and routes are not re-read automatically.
docker compose -f docker-compose.prod.yml exec app php artisan config:cache docker compose -f docker-compose.prod.yml exec app php artisan route:cache - 7
Restart nginx
Ensures it resolves the new app container.
docker compose -f docker-compose.prod.yml restart nginx - 8
Verify
Check the health endpoint returns ok before you walk away.
curl -s https://api.example.com/api/v1/health
NoteNever run a build on the host against a directory a development server is using — the two share the build output directory and will overwrite each other's chunks.