Deployment
Turno LMS is deployed on AWS EC2 with PM2 process manager, Docker for Fineract, Nginx with SSL, and local PostgreSQL.
Production URLs
| Service | URL | Port |
|---|---|---|
| Ops UI | turnoops.incentius.net | 3000 (built) |
| Prefect Dashboard | turnoprefect.incentius.net | 4201 |
| Fineract | turnomifos.incentius.net | 8443 |
| Quart API | (behind Nginx) | 5002 |
Infrastructure Components
PostgreSQL 16 (Local)
Not in Docker - runs directly on the server. Hosts both the application database and Fineract's tenant database.
Databases:
| Database | Purpose |
|---|---|
fineract | Application data (RAW, BASE, CORE schemas) |
fineract_default | Fineract's default tenant data |
fineract_tenants | Fineract's tenant metadata |
prefect_db | Prefect flow run history |
Docker (Fineract Only)
Fineract runs in Docker but connects to the local PostgreSQL:
# Start Fineract
cd ~/mifos/fineract-fork
docker compose -f docker-compose-local-postgresql.yml up -dDocker log rotation (prevents disk bloat):
// /etc/docker/daemon.json
{
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}PM2 Processes
Three processes managed by PM2:
pm2 start deploy/ecosystem.config.js| Process | Command | Log Rotation |
|---|---|---|
prefect-stack | bash start_prefect.sh | 10MB, 3 files |
quart-app | gunicorn -k uvicorn.workers.UvicornWorker -b 0.0.0.0:5002 app:app | 10MB, 3 files |
rq-worker | python worker.py | 10MB, 3 files |
Redis
redis-server # Port 6379Used by RQ for job queuing and Quart sessions.
Nginx
SSL termination and reverse proxy for all services. Key config:
# Important: increase for file uploads
client_max_body_size 20M;Initial Setup
1. Clone and Install
cd ~
git clone <repo-url> turno-fineract-lms
cd turno-fineract-lms
# Python backend
python3 -m venv venv
./venv/bin/pip install -r requirements.txt
cp config.py.sample config.py # Edit with real credentials
# Frontend — build and deploy to /var/www/html/ops_ui
bash deploy/build_ops_ui.shThe build script (deploy/build_ops_ui.sh) installs npm dependencies, builds the production bundle, and copies it to /var/www/html/ops_ui (served by Nginx).
2. Database Setup
# Create databases
sudo -u postgres createdb fineract
sudo -u postgres createdb fineract_default
sudo -u postgres createdb fineract_tenants
sudo -u postgres createdb prefect_db
# Initialize schemas and tables
./venv/bin/python scripts/init_all_tables.py3. Start Fineract
cd ~/mifos/fineract-fork
docker compose -f docker-compose-local-postgresql.yml up -d
# Register loan outstanding report (for loan-level recon)
cd ~/turno-fineract-lms
./venv/bin/python scripts/register_loan_outstanding_report.py4. Start Services
# Redis
redis-server &
# PM2 (Prefect + Quart + RQ Worker)
pm2 start deploy/ecosystem.config.js
# Register Prefect deployments
PREFECT_API_URL=http://127.0.0.1:4201/api ./venv/bin/python scripts/register_deployments.py5. Verify
# Fineract health
curl -sk https://localhost:8443/fineract-provider/api/v1/authentication \
-H "Fineract-Platform-TenantId: default" \
-d '{"username":"mifos","password":"password"}'
# Quart health
curl http://localhost:5002/
# Prefect health
curl http://localhost:4201/api/health
# dbt connection
cd dbt && ../venv/bin/dbt debugDeploying Updates
cd ~/turno-fineract-lms
git pull
# If Python deps changed
./venv/bin/pip install -r requirements.txt
# If frontend changed
bash deploy/build_ops_ui.sh
# If Prefect deployments changed
PREFECT_API_URL=http://127.0.0.1:4201/api ./venv/bin/python scripts/register_deployments.py
# Restart services
pm2 restart allRAW Layer Rewrite (One-Time Migration)
When deploying the YAML-based lender mapping rewrite:
# 1. Add file_hash column
sudo -u postgres psql -d fineract -c '
ALTER TABLE "RAW"."RAW_S3_FILE_PROCESSING_LOG"
ADD COLUMN IF NOT EXISTS file_hash VARCHAR(32);
CREATE INDEX IF NOT EXISTS idx_s3_log_file_hash
ON "RAW"."RAW_S3_FILE_PROCESSING_LOG" (file_hash);
'
# 2. Clear stale processed data
sudo -u postgres psql -d fineract -c '
TRUNCATE TABLE "CORE"."CORE_TRANSACTION_DATA_PROCESSED";
'
# 3. Reset RAW + BASE tables
./venv/bin/python scripts/reset_raw_nach_tables.py --execute
# 4. Rebuild with dbt
cd dbt && ../venv/bin/dbt run --full-refreshLog Cleanup
Automated daily cleanup prevents disk bloat:
# Add to crontab
crontab -e
0 2 * * * /home/ubuntu/turno-fineract-lms/scripts/cleanup_logs.sh --execute >> /home/ubuntu/logs/cleanup.log 2>&1Cleans: PM2 logs, Docker container logs, dbt logs, S3 temp files, old daily log directories.
Monitoring
| What | How |
|---|---|
| Service status | pm2 status |
| PM2 logs | pm2 logs |
| Fineract | docker ps + docker logs fineract-fork-fineract-1 |
| PostgreSQL | sudo -u postgres psql -d fineract -c "SELECT pg_size_pretty(pg_database_size('fineract'))" |
| Disk usage | df -h / |
| Prefect flow runs | https://turnoprefect.incentius.net |
| File processing | Ops UI > Processing Log |