# Deployment Runbook

> Audience: DevOps/developer responsible for deploying the Dockerized Proposal Management System. Run this only in an approved deployment environment.

## 1. Architecture and prerequisites

The system runs React/Vite frontend and Laravel API behind Nginx, with MySQL. The root Compose files read configuration from `apps/backend/.env`.

Before a production deployment confirm:

- Docker Engine and Docker Compose v2 are installed on the target host.
- DNS and HTTPS/TLS termination are configured for the intended domain.
- The host has persistent storage for database and uploaded files, monitoring, and backup access.
- You have a tested database backup and rollback decision owner.
- Secrets are supplied via the approved secret mechanism; no production secret is copied from `.env.example`.

## 2. Required configuration

Create `apps/backend/.env` from the template using a secure deployment process. At minimum set:

| Variable | Production expectation |
| --- | --- |
| `APP_ENV` | `production` |
| `APP_DEBUG` | `false` |
| `APP_KEY` | Unique, generated Laravel application key |
| `APP_URL` | Public HTTPS URL |
| `APP_PORT` | Intended published port, or internal port behind TLS proxy |
| `DB_*` | Production database host/database/user/password; least-privilege user |
| `CORS_ALLOWED_ORIGINS` | Explicit frontend origin list, never blanket wildcard by default |
| `VITE_API_URL` | Usually `/api` behind the same origin |
| provider credentials | Injected from secret store, never committed |

Also configure real mail, queue/worker strategy, storage/backup location and Pusher/Gemini/provider settings as needed. Verify each secret is present without printing it to terminal logs.

## 3. First production deployment

From the repository root:

```bash
make init
# Securely edit/inject apps/backend/.env now; do not retain example credentials.
make prod-build
make prod-up
```

Then validate:

```bash
docker compose --env-file apps/backend/.env -f docker-compose.prod.yml ps
docker compose --env-file apps/backend/.env -f docker-compose.prod.yml logs --tail=100
```

Open the HTTPS application URL, then check the Laravel health endpoint at `/up`. Use an authorized test account to verify login, one protected API request, upload access, notification configuration and a non-destructive report view.

## 4. Standard release procedure

1. Announce the change window and confirm the release owner, rollback owner and expected version/commit.
2. Review migrations and identify destructive, long-running or locking changes. Back up the database and uploaded files before applying them.
3. In CI, require backend tests plus clean frontend lint/build from lockfiles. Resolve any security scan failure or explicitly record a risk acceptance.
4. Fetch the approved immutable commit/image. Do not deploy a developer's unreviewed working directory.
5. Update secrets/configuration using the secret store; run a configuration diff without exposing values.
6. Build and start the production stack:

   ```bash
   make prod-build
   make prod-up
   ```

7. Run migrations once using the backend service. Use the exact Compose service name defined in the production Compose file, and run it with `--force` only after backup and approval.
8. Perform the smoke tests in the next section, observe error/queue/provider dashboards, then announce completion.

## 5. Smoke-test checklist

- [ ] HTTPS homepage loads and static assets return successfully.
- [ ] `/up` reports healthy.
- [ ] Login works for an active, least-privilege test user.
- [ ] A disabled-user token is denied on core and module routes.
- [ ] One authorised API request returns the expected response envelope.
- [ ] An unauthorised user receives 403 for an admin function.
- [ ] Existing uploaded file remains accessible only as intended.
- [ ] Notification/Pusher configuration loads without exposing a secret.
- [ ] Queue-backed export/extraction status advances or fails observably.
- [ ] Logs show no new fatal error or migration failure.

## 6. Rollback

Do not roll back blindly. First determine whether the release ran database migrations or wrote irreversible business data.

1. Put the application in maintenance mode if needed and communicate the incident.
2. Keep logs and the failing container/image available for diagnosis.
3. Restore the previously known-good application image/commit and restart the production Compose stack.
4. Reverse only a migration that has a reviewed, safe `down()` path and will not discard valid new data. Otherwise restore the approved backup through the database recovery process.
5. Validate health, authentication and a small set of critical workflows after rollback.
6. Record the incident, affected version, data decision, remediation owner and follow-up date.

## 7. Operations and maintenance

- Monitor host disk, database volume, upload volume, container health, queue failure count, 5xx rate and provider latency/cost.
- Test database and upload-file restoration on a scheduled cadence; a backup is not proven until restored successfully.
- Apply dependency/image updates through the same CI and release procedure.
- Rotate provider/API credentials using the Security Guide; redeploy affected services and verify provider connectivity.
- Never use `docker compose down -v` in production unless the specific volumes are identified, backed up, and deletion is explicitly approved. It can destroy persistent data.

## 8. Useful non-destructive commands

```bash
make logs
docker compose --env-file apps/backend/.env -f docker-compose.prod.yml ps
docker compose --env-file apps/backend/.env -f docker-compose.prod.yml logs --tail=200
docker compose --env-file apps/backend/.env -f docker-compose.prod.yml exec <backend-service> php artisan about
```

Replace `<backend-service>` with the actual production Compose service name. Do not paste environment values or secrets into support tickets or command transcripts.

