5.3 KiB
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 starton port3000
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:
curl -s http://localhost:3000/api/health
Expected response shape:
ok: trueservice: "plesk-agency-portal"timestampenvironment
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_URLmust 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:
- Checkout
- Install dependencies (
npm ci) - Lint (runs only when
package.jsoncontains alintscript) - Build application (
npm run build) - Build Docker image (tags
latestandbuild-${BUILD_NUMBER}) - Deploy over SSH to the app host
- 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 hostDEPLOY_SSH_USER- SSH user on remote hostDEPLOY_SSH_CREDENTIAL_ID- Jenkins SSH Agent credential IDDEPLOY_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.
Jenkins runtime requirements:
- The pipeline must run on a Jenkins agent with the
dockerlabel. - That agent must have Docker CLI installed and permission to access Docker daemon/socket.
rsyncandsshmust also be available on the Jenkins agent.
If Docker is missing or inaccessible, the pipeline now fails early in a dedicated preflight stage with an explicit message.
Deploy flow
Deployment is intentionally simple and bash-based:
- Jenkins builds Docker image tags locally.
- Jenkins syncs deployment files (
docker-compose.prod.yml, deploy/smoke/rollback scripts) to the remote deploy path. - Jenkins streams Docker images to the remote host (
docker save | ssh docker load). - Jenkins runs
scripts/deploy-prod.shremotely. - Script applies the selected image tag via:
APP_IMAGE=<tag> docker compose -f docker-compose.prod.yml up -d --no-build
- Script prints
docker compose psstatus.
Rollback flow
Rollback uses scripts/rollback-prod.sh with a previously built image tag:
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,servicestring)
This gives a quick post-deploy validation gate for both deploy and rollback operations.