Skip to content

Deployment Guide

Single canonical guide for running iDrv5-MyFR8 locally for development and deploying it to production.

Single canonical guide for running iDrv5-MyFR8 locally for development and deploying it to production. Replaces the four earlier guides (DEPLOYMENT_GUIDE_PRODUCTION.md, DEVELOPMENT_DEPLOYMENT_GUIDE.md, docs/DEPLOYMENT_GUIDE.md, exports/PRODUCTION_DEPLOYMENT_GUIDE.md), which are now thin pointers to this file.

ConcernValue
Production app path/var/www/idrv5 (per deploy.sh)
Production userwebapp
WSGI appmain_standalone:app (per gunicorn.conf.py)
Gunicorn bind0.0.0.0:${PORT:-5000}
Worker class (default)gevent
Reverse proxyApache2 (primary) — Nginx config also committed (alternative)
Database (dev)Supabase PostgreSQL — see .env.dev
Database (prod)Amazon RDS PostgreSQL 15+ — see .env.production
Service unitidrv5.service (systemd)

There are committed Apache and Nginx templates under apache2/ and nginx/. The canonical production stack is Apache2 + Gunicorn + RDS; the Nginx config exists for environments that already standardize on Nginx.


  • Python 3.11 (3.8+ works, 3.11 recommended)
  • Git
  • Either a local PostgreSQL 15+ install, Docker, or just network access to the Supabase dev DB credentials in .env.dev

Quick Start (uses the shared Supabase dev DB)

Section titled “Quick Start (uses the shared Supabase dev DB)”
Terminal window
# 1. Clone and enter
git clone <repo-url> idrv5 && cd idrv5
# 2. Virtual environment
python3.11 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. Install dependencies
pip install --upgrade pip
pip install -r REQUIREMENTS_DEVELOPMENT.txt
# 4. Environment
cp .env.dev .env
# 5. Run the operations portal
python main.py --port 5002
# Or run all portals via the app factory
python -c "from app_factory import create_app; app = create_app(); app.run(debug=True, port=5000)"

The dev DATABASE_URL lives in .env.dev and points at Supabase. No local PostgreSQL needed unless you want to run isolated.

Terminal window
docker run -d \
--name idrv5-postgres \
-e POSTGRES_DB=idrv5_dev \
-e POSTGRES_USER=dev_user \
-e POSTGRES_PASSWORD=dev_password \
-p 5432:5432 \
postgres:15
# Apply schema
psql postgresql://dev_user:dev_password@localhost:5432/idrv5_dev \
-f exports/01_core_schema.sql
psql postgresql://dev_user:dev_password@localhost:5432/idrv5_dev \
-f exports/02_transport_schema.sql
psql postgresql://dev_user:dev_password@localhost:5432/idrv5_dev \
-f exports/10_sample_data.sql
# Override DATABASE_URL in .env to point at this instance
  • /super-admin — auto-login as super admin (Baba / 3695)
  • /direct-operations — auto-login as operations user (test / test123)
  • /direct-compliance — auto-login as compliance officer
Terminal window
curl http://localhost:5002/api/rate-cards/
curl http://localhost:5002/health

Port already in use:

Terminal window
lsof -i :5002 # macOS/Linux
netstat -an | findstr :5002 # Windows

Module import errors: make sure your venv is active (which python should resolve inside venv/).

Database connection errors: confirm DATABASE_URL in .env is reachable (psql "$DATABASE_URL" -c 'select 1;').


Target stack: Amazon Linux 2023 (or any RHEL-family distro) + Apache2 + Gunicorn + Amazon RDS PostgreSQL.

Terminal window
sudo dnf update -y
sudo dnf install -y wget curl git unzip \
python3.11 python3.11-pip python3.11-devel \
postgresql15-devel \
gcc gcc-c++ make \
libffi-devel openssl-devel zlib-devel \
libjpeg-turbo-devel freetype-devel lcms2-devel openjpeg2-devel \
libtiff-devel libwebp-devel \
tesseract poppler-utils wkhtmltopdf \
httpd mod_ssl
sudo systemctl enable httpd

Open the firewall:

Terminal window
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Terminal window
sudo useradd -r -s /bin/bash webapp
sudo usermod -a -G apache webapp
sudo mkdir -p /var/www/idrv5
sudo chown webapp:webapp /var/www/idrv5
sudo mkdir -p /var/log/idrv5
sudo chown webapp:apache /var/log/idrv5
sudo chmod 755 /var/log/idrv5

Use the project’s deploy.sh (which expects PROD_USER=webapp and PROD_DIR=/var/www/idrv5), or rsync directly:

Terminal window
rsync -av \
--exclude='venv/' --exclude='__pycache__/' --exclude='*.pyc' \
--exclude='.git/' --exclude='instance/' --exclude='.env.dev' \
./ webapp@<server>:/var/www/idrv5/
Terminal window
aws rds create-db-instance \
--db-instance-identifier idrv5-production \
--db-instance-class db.t3.micro \
--engine postgres \
--engine-version 15.4 \
--master-username idrv5_admin \
--master-user-password '<SECURE_PASSWORD>' \
--allocated-storage 20 \
--storage-type gp2 \
--db-name idrv5_production \
--vpc-security-group-ids sg-xxxxxxxxx \
--backup-retention-period 7 \
--multi-az \
--publicly-accessible

Apply schema once the instance is reachable:

Terminal window
export DATABASE_URL='postgresql://idrv5_admin:<PASSWORD>@<RDS_ENDPOINT>:5432/idrv5_production'
psql "$DATABASE_URL" -f exports/01_core_schema.sql
psql "$DATABASE_URL" -f exports/02_transport_schema.sql
psql "$DATABASE_URL" -f exports/03_asset_management_schema.sql
psql "$DATABASE_URL" -f exports/11_reference_data.sql # optional
psql "$DATABASE_URL" -f exports/indexes_and_constraints.sql # if present
Terminal window
cd /var/www/idrv5
cp production_env_template.txt .env.production
sudo chmod 600 .env.production
sudo chown webapp:webapp .env.production

Edit .env.production with at minimum:

Terminal window
FLASK_ENV=production
FLASK_DEBUG=False
FLASK_SECRET_KEY=$(openssl rand -hex 32)
DATABASE_URL=postgresql://idrv5_admin:<PASSWORD>@<RDS_ENDPOINT>:5432/idrv5_production
PGHOST=<RDS_ENDPOINT>
PGPORT=5432
PGUSER=idrv5_admin
PGPASSWORD=<PASSWORD>
PGDATABASE=idrv5_production
SERVER_NAME=your-domain.com
PREFERRED_URL_SCHEME=https
PORT=5000
# Optional — Gunicorn knobs (gunicorn.conf.py reads these)
GUNICORN_WORKERS=
GUNICORN_WORKER_CLASS=gevent
GUNICORN_TIMEOUT=30
LOG_LEVEL=info
Terminal window
sudo -u webapp -i
cd /var/www/idrv5
python3.11 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r REQUIREMENTS_PRODUCTION.txt
exit

The repo ships gunicorn.conf.py. It binds 0.0.0.0:${PORT:-5000}, uses gevent workers, and serves main_standalone:app. All runtime knobs (GUNICORN_WORKERS, GUNICORN_WORKER_CLASS, GUNICORN_TIMEOUT, etc.) come from environment variables — set them in .env.production. Do not duplicate the config inline in systemd or in this guide.

Smoke-test it directly before moving on:

Terminal window
sudo -u webapp -i
cd /var/www/idrv5
source venv/bin/activate
set -a && source .env.production && set +a
gunicorn --config gunicorn.conf.py main_standalone:app
# expect: Listening at: http://0.0.0.0:5000

The repo ships apache2/sites-available/idrv5-myfr8.conf as a starting template (note: that template’s DocumentRoot is /opt/idrv5-myfr8/static — adjust to /var/www/idrv5/static for this deployment).

Minimal site config at /etc/httpd/conf.d/idrv5.conf:

<VirtualHost *:80>
ServerName your-domain.com
ServerAlias www.your-domain.com
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>
<VirtualHost *:443>
ServerName your-domain.com
ServerAlias www.your-domain.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/your-domain.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/your-domain.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/your-domain.com/chain.pem
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set Referrer-Policy "strict-origin-when-cross-origin"
Alias /static /var/www/idrv5/static
<Directory /var/www/idrv5/static>
Require all granted
ExpiresActive On
ExpiresDefault "access plus 1 month"
Header append Cache-Control "public"
</Directory>
ProxyPreserveHost On
ProxyPass /static !
ProxyPass / http://127.0.0.1:5000/
ProxyPassReverse / http://127.0.0.1:5000/
LimitRequestBody 52428800 # 50 MB
ErrorLog /var/log/httpd/idrv5_error.log
CustomLog /var/log/httpd/idrv5_access.log combined
</VirtualHost>

Enable and test:

Terminal window
sudo dnf install -y mod_ssl
sudo httpd -t
sudo systemctl restart httpd
Terminal window
sudo dnf install -y certbot python3-certbot-apache
sudo certbot --apache -d your-domain.com -d www.your-domain.com
sudo certbot renew --dry-run

/etc/systemd/system/idrv5.service:

[Unit]
Description=iDrv5-MyFR8 Logistics Management Platform
After=network.target
[Service]
Type=notify
User=webapp
Group=webapp
WorkingDirectory=/var/www/idrv5
Environment=PATH=/var/www/idrv5/venv/bin
EnvironmentFile=/var/www/idrv5/.env.production
ExecStart=/var/www/idrv5/venv/bin/gunicorn --config gunicorn.conf.py main_standalone:app
ExecReload=/bin/kill -s HUP $MAINPID
Restart=always
RestartSec=10
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
[Install]
WantedBy=multi-user.target

Enable:

Terminal window
sudo systemctl daemon-reload
sudo systemctl enable idrv5
sudo systemctl start idrv5
sudo systemctl status idrv5
Terminal window
sudo chown -R webapp:webapp /var/www/idrv5
sudo find /var/www/idrv5 -type d -exec chmod 755 {} \;
sudo find /var/www/idrv5 -type f -exec chmod 644 {} \;
sudo chmod 600 /var/www/idrv5/.env.production
sudo mkdir -p /var/www/idrv5/static/uploads
sudo chown webapp:apache /var/www/idrv5/static/uploads
sudo chmod 775 /var/www/idrv5/static/uploads
Terminal window
# Service health
sudo systemctl status idrv5 httpd
# Local proxy
curl -I http://127.0.0.1:5000/
curl -I http://localhost/
curl -I https://your-domain.com/
# DB connectivity from the app
sudo -u webapp -i bash -c '
cd /var/www/idrv5 && source venv/bin/activate && \
python3 -c "
import os
from sqlalchemy import create_engine, text
e = create_engine(os.environ[\"DATABASE_URL\"])
print(e.connect().execute(text(\"select version()\")).scalar())
"'

The repo also ships nginx/conf.d/idrv5.conf. If your environment standardizes on Nginx:

  1. Skip section 2.8 (Apache install/config).
  2. sudo dnf install -y nginx.
  3. Adapt nginx/conf.d/idrv5.conf:
    • Replace /usr/share/nginx/html/static with /var/www/idrv5/static.
    • Replace server_name _; with your real hostname.
    • Point proxy_pass at http://127.0.0.1:5000.
  4. SSL via certbot’s nginx plugin: sudo dnf install -y certbot python3-certbot-nginx && sudo certbot --nginx -d your-domain.com.
  5. sudo systemctl enable --now nginx.

Everything else (Gunicorn, systemd unit, RDS, env, permissions) is identical.


Terminal window
sudo journalctl -u idrv5 -f # systemd / app
sudo tail -f /var/log/httpd/idrv5_error.log # Apache
sudo tail -f /var/log/httpd/idrv5_access.log
# Gunicorn logs go to stdout/stderr (per gunicorn.conf.py) — captured by journalctl
sudo tee /usr/local/bin/idrv5-backup.sh > /dev/null << 'EOF'
#!/bin/bash
set -euo pipefail
DATE=$(date +%Y%m%d_%H%M%S)
DIR=/var/backups/idrv5
mkdir -p "$DIR"
source /var/www/idrv5/.env.production
pg_dump "$DATABASE_URL" > "$DIR/db_$DATE.sql"
tar -czf "$DIR/app_$DATE.tar.gz" -C /var/www/idrv5 .
find "$DIR" -name 'db_*.sql' -mtime +7 -delete
find "$DIR" -name 'app_*.tar.gz' -mtime +7 -delete
EOF
sudo chmod +x /usr/local/bin/idrv5-backup.sh
sudo crontab -e # add: 0 2 * * * /usr/local/bin/idrv5-backup.sh
Terminal window
sudo systemctl restart idrv5 # restart app
sudo systemctl restart httpd # restart web server
sudo certbot renew # renew SSL
sudo httpd -t # validate Apache config
sudo httpd -M | grep -E 'proxy|rewrite|ssl'

Gunicorn won’t start. Run it in the foreground to see the actual error:

Terminal window
sudo -u webapp -i
cd /var/www/idrv5 && source venv/bin/activate
set -a && source .env.production && set +a
gunicorn --config gunicorn.conf.py main_standalone:app

Apache 502/proxy errors. Check that Gunicorn is bound to 127.0.0.1:5000 (or whatever PORT you set), and that SELinux isn’t blocking proxying:

Terminal window
sudo setsebool -P httpd_can_network_connect 1
sudo setsebool -P httpd_can_network_connect_db 1

DB connection failures. Verify reachability and creds:

Terminal window
psql "$DATABASE_URL" -c 'select 1;'

Static files 404. Confirm the Alias /static /var/www/idrv5/static line and that /var/www/idrv5/static exists with apache group read permission.

Permission errors after deploy. Re-run section 2.11.


The committed reverse-proxy templates were authored against an older /opt/idrv5-myfr8 layout while deploy.sh and current systemd target /var/www/idrv5. When using the templates in apache2/ or nginx/ directly, search-and-replace /opt/idrv5-myfr8/var/www/idrv5 before applying. The canonical paths in this guide reflect what deploy.sh actually does.