150 lines
4.9 KiB
Markdown
150 lines
4.9 KiB
Markdown
# Production Deployment Runtime Foundation
|
|
|
|
This project can be deployed in a dedicated Proxmox container using Docker Compose. The files added here establish a minimal production runtime baseline without changing application business logic or Supabase security boundaries.
|
|
|
|
## Dockerfile purpose
|
|
|
|
The `Dockerfile` uses a multi-stage build on Node 20:
|
|
|
|
- installs dependencies
|
|
- runs `next build`
|
|
- prepares a lean runtime image with production dependencies
|
|
- runs the app with `next start` on port `3000`
|
|
|
|
This keeps the runtime image small and focused on serving the built Next.js app.
|
|
|
|
## docker-compose.prod.yml purpose
|
|
|
|
`docker-compose.prod.yml` defines one service:
|
|
|
|
- service name: `web`
|
|
- container name: `plesk-agency-portal`
|
|
- restart policy: `unless-stopped`
|
|
- environment from `.env`
|
|
- port mapping `3000:3000`
|
|
- health check against `/api/health`
|
|
|
|
## External Supabase requirement
|
|
|
|
Supabase remains external to this application container. The app container must only connect to Supabase via environment variables. No local Supabase container is included in this production compose file.
|
|
|
|
## Health endpoint usage
|
|
|
|
`GET /api/health` provides a fast, dependency-light runtime check for container orchestration and uptime checks.
|
|
|
|
Example:
|
|
|
|
```bash
|
|
curl -s http://localhost:3000/api/health
|
|
```
|
|
|
|
Expected response shape:
|
|
|
|
- `ok: true`
|
|
- `service: "plesk-agency-portal"`
|
|
- `timestamp`
|
|
- `environment`
|
|
|
|
## Production environment expectations
|
|
|
|
Before deploying, set production-safe values in `.env` (using `.env.example` as the template), including:
|
|
|
|
- app URL and `NODE_ENV`
|
|
- Supabase public and server credentials
|
|
- Plesk credential encryption key
|
|
- Stripe secrets
|
|
- cron/job shared secrets
|
|
|
|
Keep secrets out of source control.
|
|
|
|
## Reverse proxy assumptions (Nginx Proxy Manager)
|
|
|
|
This app is expected to run behind a reverse proxy in production.
|
|
|
|
- TLS is terminated at the proxy.
|
|
- Proxy forwards standard host/protocol headers (`Host`, `X-Forwarded-Proto`, `X-Forwarded-Host`).
|
|
- Public origin is configured explicitly with `NEXT_PUBLIC_APP_URL`.
|
|
|
|
To keep callback behavior stable behind proxy layers, auth redirect responses use the configured application origin from environment configuration rather than relying only on request host detection.
|
|
|
|
## Callback URL expectations
|
|
|
|
- `NEXT_PUBLIC_APP_URL` must be set to the public HTTPS URL used by users.
|
|
- Supabase auth redirect/callback configuration must allow:
|
|
- `<NEXT_PUBLIC_APP_URL>/auth/callback`
|
|
|
|
The callback route also validates the `next` parameter as an internal path to prevent unsafe external redirects.
|
|
|
|
## Cron protection expectations
|
|
|
|
Operational cron endpoints are protected with a shared secret.
|
|
|
|
- Required header: `x-cron-secret`
|
|
- Server secret source: `CRON_SHARED_SECRET`
|
|
- Backward compatibility fallback: `CRON_SECRET`
|
|
|
|
For new production setups, set `CRON_SHARED_SECRET` and use that value for all cron callers.
|
|
|
|
## Jenkins pipeline flow
|
|
|
|
The repository includes a declarative `Jenkinsfile` with the following stages:
|
|
|
|
1. **Checkout**
|
|
2. **Install dependencies** (`npm ci`)
|
|
3. **Lint** (runs only when `package.json` contains a `lint` script)
|
|
4. **Build application** (`npm run build`)
|
|
5. **Build Docker image** (tags `latest` and `build-${BUILD_NUMBER}`)
|
|
6. **Deploy** over SSH to the app host
|
|
7. **Smoke check** against `/api/health`
|
|
|
|
## Required Jenkins configuration
|
|
|
|
Configure these values in Jenkins job/folder/global environment or credentials-backed environment variables:
|
|
|
|
- `DEPLOY_SSH_HOST` - remote app host
|
|
- `DEPLOY_SSH_USER` - SSH user on remote host
|
|
- `DEPLOY_SSH_CREDENTIAL_ID` - Jenkins SSH Agent credential ID
|
|
- `DEPLOY_PATH` - remote deployment path (for example `/opt/plesk-agency-portal`)
|
|
- `APP_BASE_URL` - externally reachable app URL used by smoke check
|
|
|
|
The pipeline assumes Jenkins credentials are managed outside this repository.
|
|
|
|
## Deploy flow
|
|
|
|
Deployment is intentionally simple and bash-based:
|
|
|
|
1. Jenkins builds Docker image tags locally.
|
|
2. Jenkins syncs deployment files (`docker-compose.prod.yml`, deploy/smoke/rollback scripts) to the remote deploy path.
|
|
3. Jenkins streams Docker images to the remote host (`docker save | ssh docker load`).
|
|
4. Jenkins runs `scripts/deploy-prod.sh` remotely.
|
|
5. Script applies the selected image tag via:
|
|
|
|
```bash
|
|
APP_IMAGE=<tag> docker compose -f docker-compose.prod.yml up -d --no-build
|
|
```
|
|
|
|
6. Script prints `docker compose ps` status.
|
|
|
|
## Rollback flow
|
|
|
|
Rollback uses `scripts/rollback-prod.sh` with a previously built image tag:
|
|
|
|
```bash
|
|
DEPLOY_PATH=/opt/plesk-agency-portal \
|
|
APP_BASE_URL=https://your-app.example.com \
|
|
scripts/rollback-prod.sh plesk-agency-portal:build-123
|
|
```
|
|
|
|
Rollback redeploys that image with compose, then immediately reruns smoke checks.
|
|
|
|
## Smoke check behavior
|
|
|
|
`scripts/smoke-check.sh`:
|
|
|
|
- accepts a base URL argument
|
|
- requests `${BASE_URL}/api/health`
|
|
- fails on non-200 responses
|
|
- fails on invalid JSON or missing expected fields (`ok: true`, `service` string)
|
|
|
|
This gives a quick post-deploy validation gate for both deploy and rollback operations.
|