Skip to main content

Overview

CONFOR uses PostgreSQL 15+ with PostGIS extensions for geospatial data processing. This guide covers database setup using Docker Compose or manual installation.

Prerequisites

PostgreSQL 15+

Relational database engine

PostGIS Extension

Spatial database capabilities

Redis 7+

Caching and session storage

Node.js 20+

For running Prisma CLI

Quick Start with Docker

The fastest way to get started is using Docker Compose.

1. Start Database Services

# Start PostgreSQL and Redis
docker compose up -d

# Verify services are running
docker compose ps

# Expected output:
NAME                IMAGE                PORTS
postgres            postgres:15-alpine   0.0.0.0:5432->5432/tcp
redis               redis:7-alpine       0.0.0.0:6379->6379/tcp

2. Docker Compose Configuration

The docker-compose.yml file defines the database services:
docker-compose.yml
services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: app_dev
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    ports:
      - "6379:6379"
    volumes:
      - redisdata:/data

volumes:
  pgdata:
  redisdata:
Data is persisted in Docker volumes (pgdata and redisdata). Containers can be safely stopped and restarted without data loss.

Manual PostgreSQL Installation

If you prefer not to use Docker:
  1. Download PostgreSQL 15+ from postgresql.org
  2. Run the installer and include Stack Builder
  3. In Stack Builder, select and install PostGIS extension
  4. Set password for postgres user during installation
  5. Verify installation:
psql --version
# Should show: psql (PostgreSQL) 15.x

Prisma Setup

1. Install Dependencies

npm install

2. Configure Database URL

Create a .env file in the project root:
.env
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/app_dev"
NEXTAUTH_SECRET="your-secret-key-min-32-characters"

3. Generate Prisma Client

npm run db:generate
This generates the Prisma client based on your schema.prisma file.

4. Run Migrations

# Apply all pending migrations
npm run db:migrate

# This will:
# - Create database tables
# - Install PostGIS and other extensions
# - Set up partitioning for audit_logs
# - Create indexes and constraints

5. Seed Database

npm run db:seed
This creates:
  • Default organization (default-org)
  • System roles: SUPER_ADMIN, ADMIN, MANAGER, USER
  • Permissions for all modules
  • Admin user: [email protected] / Admin1234
  • Base system configuration
Change the default admin password immediately after first login in production environments.

Database Schema Overview

Core Tables

TablePurpose
UserUser accounts and authentication
OrganizationMulti-tenant organization data
RoleRole definitions
PermissionGranular permissions
ModuleSystem modules (users, dashboard, etc.)
audit_logsActivity audit trail (partitioned by month)

Forest Management Tables

TablePurpose
PatrimonyLevel2Level 2 forest units (finca, predio, hato)
PatrimonyLevel3Level 3 forest units (lote, zona, bloque)
PatrimonyLevel4Level 4 forest units (rodal) - includes geometry
BiologicalAssetForest biological assets
SpeciesTree and plant species
ManagementSchemeForest management schemes
LandUseTypeLand use classifications

Geospatial Tables

TablePurpose
GeospatialImportJobTracks shapefile import jobs
GeospatialRecalcJobSurface recalculation jobs
GeospatialVariationJobGeometry variation jobs
PolygonGeometryStores WKB geometry data

PostGIS Extensions

The first migration installs required PostgreSQL extensions:
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";     -- UUID generation
CREATE EXTENSION IF NOT EXISTS "postgis";        -- Spatial data support
CREATE EXTENSION IF NOT EXISTS "pg_trgm";        -- Trigram text search
CREATE EXTENSION IF NOT EXISTS "btree_gist";     -- Indexing support

Audit Log Partitioning

CONFOR uses monthly table partitioning for the audit_logs table to improve performance.

Automatic Partition Creation

A trigger automatically creates new partitions when inserting audit logs:
CREATE OR REPLACE FUNCTION create_audit_partition()
RETURNS TRIGGER AS $$
DECLARE
  partition_date TEXT;
  partition_name TEXT;
  start_date DATE;
  end_date DATE;
BEGIN
  partition_date := TO_CHAR(NEW.created_at, 'YYYY_MM');
  partition_name := 'audit_logs_' || partition_date;
  start_date := DATE_TRUNC('month', NEW.created_at);
  end_date := start_date + INTERVAL '1 month';
  
  -- Create partition if it doesn't exist
  EXECUTE format(
    'CREATE TABLE IF NOT EXISTS %I PARTITION OF audit_logs
     FOR VALUES FROM (%L) TO (%L)',
    partition_name, start_date, end_date
  );
  
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

Manual Partition Management

You can also create partitions manually using the provided script:
npx tsx scripts/create-audit-partition.ts

Database Maintenance

Backup Database

# Backup to SQL file
docker compose exec postgres pg_dump -U postgres app_dev > backup_$(date +%Y%m%d).sql

# Backup with compression
docker compose exec postgres pg_dump -U postgres app_dev | gzip > backup_$(date +%Y%m%d).sql.gz

Restore Database

# Restore from SQL file
docker compose exec -T postgres psql -U postgres app_dev < backup.sql

# Restore from compressed backup
gunzip -c backup.sql.gz | docker compose exec -T postgres psql -U postgres app_dev

Vacuum and Analyze

Regularly optimize database performance:
-- Full vacuum (reclaim storage)
VACUUM FULL;

-- Update statistics for query planner
ANALYZE;

-- Both operations
VACUUM FULL ANALYZE;

-- Auto-vacuum specific table
VACUUM ANALYZE audit_logs;

Troubleshooting

Symptoms: ECONNREFUSED or Connection refusedSolutions:
# Check if PostgreSQL is running
docker compose ps postgres
# or for manual installation
sudo systemctl status postgresql

# Check port is listening
netstat -an | grep 5432
# or
ss -tulpn | grep 5432

# Restart service
docker compose restart postgres
# or
sudo systemctl restart postgresql
Symptoms: ERROR: extension "postgis" is not availableSolutions:
# For Docker (should be included in postgres image)
docker compose exec postgres psql -U postgres -d app_dev -c "CREATE EXTENSION postgis;"

# For manual installation, install PostGIS package
# Ubuntu/Debian:
sudo apt-get install postgresql-15-postgis-3

# Then enable in database:
psql -U postgres -d app_dev -c "CREATE EXTENSION postgis;"
Symptoms: permission denied to create extensionSolutions:
# Grant superuser to your database user
psql -U postgres -c "ALTER USER your_user WITH SUPERUSER;"

# Or connect as postgres user and run migrations
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/app_dev" npx prisma migrate deploy
Symptoms: PrismaClientInitializationError: Prisma Client could not locate the Query EngineSolutions:
# Regenerate Prisma client
npm run db:generate

# Clear node_modules and reinstall
rm -rf node_modules .next
npm install
npm run db:generate
Symptoms: ERROR: no partition of relation "audit_logs" foundSolutions:
# Manually create partition for current month
npx tsx scripts/create-audit-partition.ts

# Or create via SQL
psql -U postgres -d app_dev <<SQL
CREATE TABLE IF NOT EXISTS audit_logs_2026_03 PARTITION OF audit_logs
FOR VALUES FROM ('2026-03-01') TO ('2026-04-01');
SQL
Solutions:
-- Check missing indexes
SELECT schemaname, tablename, indexname
FROM pg_indexes
WHERE schemaname = 'public'
ORDER BY tablename, indexname;

-- Analyze table statistics
ANALYZE VERBOSE PatrimonyLevel4;

-- Check slow queries
SELECT query, calls, total_exec_time, mean_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
Consider adding indexes:
CREATE INDEX CONCURRENTLY idx_level4_org ON "PatrimonyLevel4"("organizationId");
CREATE INDEX CONCURRENTLY idx_level4_level3 ON "PatrimonyLevel4"("level3Id");

Next Steps

Environment Variables

Configure all environment variables

Worker Setup

Set up geospatial processing workers

Forest Patrimony

Forest hierarchy data models

Installation Guide

Complete installation instructions

Build docs developers (and LLMs) love