Files
pleskSaas/docs/deployment-production.md

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 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:

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.

Jenkins runtime requirements:

  • The pipeline must run on a Jenkins agent with the docker label.
  • That agent must have Docker CLI installed and permission to access Docker daemon/socket.
  • rsync and ssh must 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:

  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:
APP_IMAGE=<tag> docker compose -f docker-compose.prod.yml up -d --no-build
  1. Script prints docker compose ps status.

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, service string)

This gives a quick post-deploy validation gate for both deploy and rollback operations.