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

376 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# HOA LedgerIQ — Deployment Guide
**Version:** 2026.3.2 (beta)
**Last updated:** 2026-03-02
---
## Table of Contents
1. [Prerequisites](#prerequisites)
2. [Deploy to a Fresh Docker Server](#deploy-to-a-fresh-docker-server)
3. [Backup the Local Test Database](#backup-the-local-test-database)
4. [Restore a Backup into the Staged Environment](#restore-a-backup-into-the-staged-environment)
5. [Running Migrations on the Staged Environment](#running-migrations-on-the-staged-environment)
6. [Verifying the Deployment](#verifying-the-deployment)
7. [Environment Variable Reference](#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
```bash
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:
```bash
cp .env.example .env
nano .env # or vi, your choice
```
**Required changes from defaults:**
```dotenv
# --- 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:
> ```bash
> openssl rand -hex 32 # good for JWT_SECRET
> openssl rand -base64 24 # good for POSTGRES_PASSWORD
> ```
### 3. Build and start the stack
```bash
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
```bash
docker compose ps
```
All five containers should show `Up` (postgres and redis should also show
`(healthy)`). If the backend is restarting, check logs:
```bash
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:
```bash
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
### Full database dump (recommended)
From your **local development machine** where the app is currently running:
```bash
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:
```bash
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):
```bash
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:**
> ```bash
> 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
```bash
scp hoafinance_backup.dump user@staging-server:/opt/hoa-ledgeriq/
```
### 2. Ensure the stack is running
```bash
cd /opt/hoa-ledgeriq
docker compose up -d
```
### 3. Drop and recreate the database (clean slate)
```bash
# 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
```bash
# 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
```bash
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:
```bash
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:
```bash
ls -la db/migrations/
```
Apply them in order:
```bash
# 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:
```bash
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
```bash
# 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
```bash
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.