Files
HOA_Financial_Platform/docs/DEPLOYMENT.md
olsch01 a550a8d0be Add deployment guide for staging Docker servers with DB backup/restore
Covers fresh server setup, environment configuration, database backup
(full and per-tenant), restore into staged environment, migration
execution, and verification steps.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 14:09:32 -05:00

10 KiB
Raw Blame History

HOA LedgerIQ — Deployment Guide

Version: 2026.3.2 (beta) Last updated: 2026-03-02


Table of Contents

  1. Prerequisites
  2. Deploy to a Fresh Docker Server
  3. Backup the Local Test Database
  4. Restore a Backup into the Staged Environment
  5. Running Migrations on the Staged Environment
  6. Verifying the Deployment
  7. Environment Variable Reference

Prerequisites

On the target server, ensure the following are installed:

Tool Minimum Version
Docker Engine 24+
Docker Compose v2+
Git 2.x
psql (client) 15+ (optional, for manual DB work)

The app runs five containers — nginx, backend (NestJS), frontend (Vite/React), PostgreSQL 15, and Redis 7. Total memory footprint is roughly 12 GB idle.


Deploy to a Fresh Docker Server

1. Clone the repository

ssh your-staging-server

git clone <repo-url> /opt/hoa-ledgeriq
cd /opt/hoa-ledgeriq

2. Create the environment file

Copy the example and fill in real values:

cp .env.example .env
nano .env          # or vi, your choice

Required changes from defaults:

# --- CHANGE THESE ---
POSTGRES_PASSWORD=<strong-random-password>
JWT_SECRET=<random-64-char-string>

# Database URL must match the password above
DATABASE_URL=postgresql://hoafinance:<same-password>@postgres:5432/hoafinance

# AI features (get a key from build.nvidia.com)
AI_API_KEY=nvapi-xxxxxxxxxxxx

# --- Usually fine as-is ---
POSTGRES_USER=hoafinance
POSTGRES_DB=hoafinance
REDIS_URL=redis://redis:6379
NODE_ENV=development        # keep as development for staging
AI_API_URL=https://integrate.api.nvidia.com/v1
AI_MODEL=qwen/qwen3.5-397b-a17b
AI_DEBUG=false

Tip: Generate secrets quickly:

openssl rand -hex 32    # good for JWT_SECRET
openssl rand -base64 24 # good for POSTGRES_PASSWORD

3. Build and start the stack

docker compose up -d --build

This will:

  • Build the backend and frontend images
  • Pull postgres:15-alpine, redis:7-alpine, and nginx:alpine
  • Initialize the PostgreSQL database with the shared schema (db/init/00-init.sql)
  • Start all five services on the hoanet bridge network

4. Wait for healthy services

docker compose ps

All five containers should show Up (postgres and redis should also show (healthy)). If the backend is restarting, check logs:

docker compose logs backend --tail=50

5. (Optional) Seed with demo data

If deploying a fresh environment for testing and you want the Sunrise Valley HOA demo tenant:

docker compose exec -T postgres psql -U hoafinance -d hoafinance < db/seed/seed.sql

This creates:

  • Platform admin: admin@hoaledgeriq.com / password123
  • Tenant admin: admin@sunrisevalley.org / password123
  • Tenant viewer: viewer@sunrisevalley.org / password123

6. Access the application

Service URL
App (UI) http://<server-ip>
API http://<server-ip>/api
Postgres <server-ip>:5432 (direct)

Note: For production, add an SSL-terminating proxy (Caddy, Traefik, or an nginx TLS config) in front of port 80.


Backup the Local Test Database

From your local development machine where the app is currently running:

cd /path/to/HOA_Financial_Platform

# Dump the entire database (all schemas, roles, data)
docker compose exec -T postgres pg_dump \
  -U hoafinance \
  -d hoafinance \
  --no-owner \
  --no-privileges \
  --format=custom \
  -f /tmp/hoafinance_backup.dump

# Copy the dump file out of the container
docker compose cp postgres:/tmp/hoafinance_backup.dump ./hoafinance_backup.dump

The --format=custom flag produces a compressed binary format that supports selective restore. The file is typically 5080% smaller than plain SQL.

Alternative: Plain SQL dump

If you prefer a human-readable SQL file:

docker compose exec -T postgres pg_dump \
  -U hoafinance \
  -d hoafinance \
  --no-owner \
  --no-privileges \
  > hoafinance_backup.sql

Backup a single tenant schema

To export just one tenant (e.g., Pine Creek HOA):

docker compose exec -T postgres pg_dump \
  -U hoafinance \
  -d hoafinance \
  --no-owner \
  --no-privileges \
  --schema=tenant_pine_creek_hoa_q33i \
  > pine_creek_backup.sql

Finding a tenant's schema name:

docker compose exec -T postgres psql -U hoafinance -d hoafinance \
  -c "SELECT name, schema_name FROM shared.organizations WHERE status = 'active';"

Restore a Backup into the Staged Environment

1. Transfer the backup to the staging server

scp hoafinance_backup.dump user@staging-server:/opt/hoa-ledgeriq/

2. Ensure the stack is running

cd /opt/hoa-ledgeriq
docker compose up -d

3. Drop and recreate the database (clean slate)

# Connect to postgres and reset the database
docker compose exec -T postgres psql -U hoafinance -d postgres -c "
  SELECT pg_terminate_backend(pid)
  FROM pg_stat_activity
  WHERE datname = 'hoafinance' AND pid <> pg_backend_pid();
"
docker compose exec -T postgres dropdb -U hoafinance hoafinance
docker compose exec -T postgres createdb -U hoafinance hoafinance

4a. Restore from custom-format dump

# Copy the dump into the container
docker compose cp hoafinance_backup.dump postgres:/tmp/hoafinance_backup.dump

# Restore
docker compose exec -T postgres pg_restore \
  -U hoafinance \
  -d hoafinance \
  --no-owner \
  --no-privileges \
  /tmp/hoafinance_backup.dump

4b. Restore from plain SQL dump

docker compose exec -T postgres psql \
  -U hoafinance \
  -d hoafinance \
  < hoafinance_backup.sql

5. Restart the backend

After restoring, restart the backend so NestJS re-establishes its connection pool and picks up the restored schemas:

docker compose restart backend

Running Migrations on the Staged Environment

Migrations live in db/migrations/ and are numbered sequentially. After restoring an older backup, you may need to apply newer migrations.

Check which migrations exist:

ls -la db/migrations/

Apply them in order:

# Run all migrations sequentially
for f in db/migrations/*.sql; do
  echo "Applying $f ..."
  docker compose exec -T postgres psql \
    -U hoafinance \
    -d hoafinance \
    < "$f"
done

Or apply a specific migration:

docker compose exec -T postgres psql \
  -U hoafinance \
  -d hoafinance \
  < db/migrations/010-health-scores.sql

Note: Migrations are idempotent where possible (IF NOT EXISTS, DO $$ ... $$ blocks), so re-running one that has already been applied is generally safe.


Verifying the Deployment

Quick health checks

# Backend is responding
curl -s http://localhost/api/auth/login | head -c 100

# Database is accessible
docker compose exec -T postgres psql -U hoafinance -d hoafinance \
  -c "SELECT count(*) AS tenants FROM shared.organizations WHERE status = 'active';"

# Redis is working
docker compose exec -T redis redis-cli ping

Full smoke test

  1. Open http://<server-ip> in a browser
  2. Log in with a known account
  3. Navigate to Dashboard — verify health scores load
  4. Navigate to Capital Planning — verify Kanban columns render
  5. Navigate to Projects — verify project list loads
  6. Check the Settings page — version should read 2026.3.2 (beta)

View logs

docker compose logs -f              # all services
docker compose logs -f backend      # backend only
docker compose logs -f postgres     # database only

Environment Variable Reference

Variable Required Description
POSTGRES_USER Yes PostgreSQL username
POSTGRES_PASSWORD Yes PostgreSQL password (change from default)
POSTGRES_DB Yes Database name
DATABASE_URL Yes Full connection string for the backend
REDIS_URL Yes Redis connection string
JWT_SECRET Yes Secret for signing JWT tokens (change from default)
NODE_ENV Yes development or production
AI_API_URL Yes OpenAI-compatible inference endpoint
AI_API_KEY Yes API key for AI provider (Nvidia)
AI_MODEL Yes Model identifier for AI calls
AI_DEBUG No Set true to log raw AI prompts/responses

Architecture Overview

                    ┌─────────────┐
  Browser ────────► │  nginx :80  │
                    └──────┬──────┘
                  ┌────────┴────────┐
                  ▼                 ▼
          ┌──────────────┐  ┌──────────────┐
          │ backend :3000│  │frontend :5173│
          │   (NestJS)   │  │ (Vite/React) │
          └──────┬───────┘  └──────────────┘
            ┌────┴────┐
            ▼         ▼
    ┌────────────┐ ┌───────────┐
    │postgres:5432│ │redis :6379│
    │ (PG 15)    │ │ (Redis 7) │
    └────────────┘ └───────────┘

Multi-tenant isolation: Each HOA organization gets its own PostgreSQL schema (e.g., tenant_pine_creek_hoa_q33i). The shared schema holds cross-tenant tables (users, organizations, market rates). Tenant context is resolved from the JWT token on every API request.