Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/amankiit/OmniEHR/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Deploying OmniEHR to production requires careful attention to security, reliability, and compliance. This guide covers production-ready deployment configurations, infrastructure recommendations, and HIPAA-aligned security hardening.
HIPAA Compliance Notice: This codebase implements technical safeguards (access control, encryption, audit trails), but HIPAA compliance requires more than code. You must also implement:
  • Business Associate Agreements (BAAs)
  • Policies and procedures
  • Risk assessments
  • Incident response plans
  • Staff training
  • Secure infrastructure operations

Pre-Deployment Checklist

Before deploying to production, ensure you have:
  • Generated strong, unique secrets for all environment variables
  • Set up secure secrets management (AWS Secrets Manager, HashiCorp Vault, etc.)
  • Configured MongoDB with authentication, TLS, and backups
  • Obtained SSL/TLS certificates for HTTPS
  • Set up monitoring and alerting
  • Configured audit log retention and SIEM integration
  • Prepared incident response procedures
  • Reviewed and signed BAAs with all service providers
  • Conducted security assessment and penetration testing
  • Set up backup and disaster recovery procedures

Infrastructure Requirements

Minimum Production Requirements

Server:
  • Node.js v18+ runtime
  • 2 GB RAM minimum (4+ GB recommended)
  • 2 CPU cores minimum
  • 20 GB disk space for application and logs
Database:
  • MongoDB 7.0+
  • 4 GB RAM minimum (8+ GB recommended)
  • 2 CPU cores minimum
  • SSD storage with automatic backups
  • Replica set for high availability (3+ nodes)
Network:
  • TLS 1.2+ for all connections
  • Firewall rules limiting access to necessary ports only
  • DDoS protection
  • CDN for static assets (optional)
                                  ┌─────────────────┐
                                  │   CloudFront    │
                                  │   or CDN        │
                                  └────────┬────────┘

                                           │ HTTPS

                                  ┌────────▼────────┐
                                  │  Load Balancer  │
                                  │   (ALB/NLB)     │
                                  └────────┬────────┘

                        ┌──────────────────┼──────────────────┐
                        │                  │                  │
               ┌────────▼────────┐┌────────▼────────┐┌────────▼────────┐
               │   API Server    ││   API Server    ││   API Server    │
               │   (Node.js)     ││   (Node.js)     ││   (Node.js)     │
               └────────┬────────┘└────────┬────────┘└────────┬────────┘
                        │                  │                  │
                        └──────────────────┼──────────────────┘

                                  ┌────────▼────────┐
                                  │   MongoDB       │
                                  │   Replica Set   │
                                  │   (3+ nodes)    │
                                  └─────────────────┘
Key components:
  • CDN/CloudFront: Serve static client assets with caching
  • Load Balancer: Distribute traffic across multiple API servers
  • API Servers: Multiple Node.js instances for redundancy
  • MongoDB Replica Set: High availability database cluster

Deployment Options

Option 1: Platform as a Service (PaaS)

Quickest deployment with managed infrastructure.
Pros:
  • Automatic HTTPS
  • Easy MongoDB integration
  • Built-in monitoring
  • Free tier available
Setup:
1

Create MongoDB instance

Use Render’s managed MongoDB or connect to MongoDB Atlas
2

Deploy server

render.yaml
services:
  - type: web
    name: omniehr-api
    env: node
    buildCommand: npm install --workspace server
    startCommand: npm run start --workspace server
    envVars:
      - key: NODE_ENV
        value: production
      - key: PORT
        value: 4000
      - key: MONGODB_URI
        sync: false
      - key: JWT_SECRET
        generateValue: true
      - key: JWT_EXPIRES_IN
        value: 1h
      - key: PHI_ENCRYPTION_KEY
        sync: false
      - key: CORS_ORIGIN
        value: https://your-frontend-url.vercel.app
3

Deploy client

Deploy to Vercel, Netlify, or Render static siteSet VITE_API_BASE_URL to your Render API URL
Current production deployment uses Render for API (https://omniehr.onrender.com) and Vercel for client (https://omni-ehr.vercel.app)

Option 2: Self-Hosted (VPS/Bare Metal)

Full control but requires more operational expertise.
1

Provision server

Use Ubuntu 22.04 LTS or similar. Harden OS:
# Update system
sudo apt update && sudo apt upgrade -y

# Install Node.js 18
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install -y nodejs

# Install MongoDB
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
sudo apt update
sudo apt install -y mongodb-org

# Install nginx
sudo apt install -y nginx certbot python3-certbot-nginx

# Install PM2 for process management
sudo npm install -g pm2
2

Configure MongoDB

Edit /etc/mongod.conf:
net:
  port: 27017
  bindIp: 127.0.0.1
  tls:
    mode: requireTLS
    certificateKeyFile: /etc/ssl/mongodb.pem

security:
  authorization: enabled

storage:
  dbPath: /var/lib/mongodb
  journal:
    enabled: true
  engine: wiredTiger
  wiredTiger:
    engineConfig:
      cacheSizeGB: 2

systemLog:
  destination: file
  path: /var/log/mongodb/mongod.log
  logAppend: true
Create admin user:
mongosh
use admin
db.createUser({
  user: "admin",
  pwd: "strong-password-here",
  roles: ["userAdminAnyDatabase", "dbAdminAnyDatabase", "readWriteAnyDatabase"]
})
3

Deploy application

# Clone repository
git clone <repo-url> /opt/omniehr
cd /opt/omniehr

# Install dependencies
npm install

# Create production .env
sudo nano server/.env
# (Add all production variables)

# Start with PM2
pm2 start server/src/server.js --name omniehr-api
pm2 save
pm2 startup
4

Configure nginx reverse proxy

/etc/nginx/sites-available/omniehr
server {
    listen 80;
    server_name api.omniehr.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name api.omniehr.com;

    ssl_certificate /etc/letsencrypt/live/api.omniehr.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.omniehr.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    location / {
        proxy_pass http://127.0.0.1:4000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}
Enable and restart:
sudo ln -s /etc/nginx/sites-available/omniehr /etc/nginx/sites-enabled/
sudo certbot --nginx -d api.omniehr.com
sudo systemctl restart nginx

Security Hardening

1. Environment Variables & Secrets Management

Never store secrets in .env files in production. Use a secrets management service.
Best practices:
# Store secrets
aws secretsmanager create-secret \
  --name omniehr/prod/env \
  --secret-string '{"JWT_SECRET":"...","PHI_ENCRYPTION_KEY":"..."}'

# Retrieve in application startup
const secrets = JSON.parse(
  await secretsManager.getSecretValue({SecretId: 'omniehr/prod/env'}).promise()
);

2. Database Security

MongoDB hardening checklist:
  • Enable authentication (security.authorization: enabled)
  • Use TLS for all connections (net.tls.mode: requireTLS)
  • Create least-privilege users (separate users for app, backup, monitoring)
  • Enable audit logging for HIPAA compliance
  • Restrict network access (bind to private IP, use VPC)
  • Enable encryption at rest (MongoDB Enterprise or cloud provider encryption)
  • Set up automated backups with encryption
  • Configure replica set for high availability
  • Monitor slow queries and set up alerts
  • Regularly update MongoDB to latest stable version
Example production connection string:
MONGODB_URI=mongodb://app-user:STRONG_PASSWORD@node1.internal:27017,node2.internal:27017,node3.internal:27017/ehr?replicaSet=rs0&authSource=admin&tls=true&tlsCAFile=/path/to/ca.pem

3. Application Security

OmniEHR includes several security features out of the box: Implemented in server/src/app.js:
// Helmet: Sets secure HTTP headers
app.use(helmet());

// CORS: Restricts origins
app.use(cors({
  origin: env.corsOrigin,
  credentials: true
}));

// Rate limiting: Prevents brute force
app.use("/api/auth", rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // 100 requests per window
}));

// JSON limit: Prevents large payload attacks
app.use(express.json({ limit: "1mb" }));

// Request logging: Audit trail
app.use(morgan("combined"));

// Audit middleware: Logs all FHIR/admin operations
app.use(requestAuditTrail);
Additional hardening recommendations:
1

Enable HTTPS only

Enforce HTTPS at load balancer or reverse proxy level
// Redirect HTTP to HTTPS
app.use((req, res, next) => {
  if (req.header('x-forwarded-proto') !== 'https' && env.nodeEnv === 'production') {
    return res.redirect(`https://${req.header('host')}${req.url}`);
  }
  next();
});
2

Implement token refresh & rotation

Current implementation uses long-lived access tokens. For production:
  • Reduce JWT_EXPIRES_IN to 15-60 minutes
  • Implement refresh token flow
  • Store refresh tokens in database with ability to revoke
  • Rotate refresh tokens on each use
3

Add MFA for admin users

Implement TOTP-based two-factor authentication:
  • Use otplib or speakeasy library
  • Store MFA secret encrypted per user
  • Require MFA for admin and auditor roles
  • Provide backup codes
4

Implement session management

  • Store active sessions in Redis
  • Allow users to view and revoke sessions
  • Implement session timeout (absolute and idle)
  • Clear sessions on password change
5

Add request signing for critical operations

For destructive operations (delete, update PHI):
  • Require re-authentication
  • Implement request signing
  • Log all changes with before/after state

4. PHI Encryption & Key Management

Current implementation uses application-level encryption (AES-256-GCM) in server/src/services/cryptoService.js. Production enhancements:
Use cloud provider KMS for key encryption keys:
import { KMSClient, DecryptCommand } from "@aws-sdk/client-kms";

// Encrypted PHI_ENCRYPTION_KEY in environment
const encryptedKey = process.env.ENCRYPTED_PHI_KEY;

// Decrypt with KMS
const kms = new KMSClient({ region: "us-east-1" });
const { Plaintext } = await kms.send(new DecryptCommand({
  CiphertextBlob: Buffer.from(encryptedKey, 'base64')
}));

const phiKey = Buffer.from(Plaintext);
Benefits:
  • Key never stored in plaintext
  • Audit trail for key usage
  • Automatic key rotation
  • HSM-backed security (FIPS 140-2 Level 3)

5. Audit Logging & SIEM Integration

OmniEHR logs all FHIR and admin operations via requestAuditTrail middleware. Current implementation (server/src/middleware/audit.js):
  • Logs to MongoDB AuditEvent collection
  • Captures: user, action, resource, timestamp, IP address
  • Available for review in admin UI
Production enhancements:
1

Immutable audit logs

Send audit events to immutable storage:
import AWS from 'aws-sdk';
const s3 = new AWS.S3();

export const auditToS3 = async (event) => {
  await s3.putObject({
    Bucket: 'omniehr-audit-logs',
    Key: `audit/${new Date().toISOString()}/${event.id}.json`,
    Body: JSON.stringify(event),
    ServerSideEncryption: 'aws:kms'
  }).promise();
};
2

SIEM integration

Forward logs to Security Information and Event Management:Splunk:
import SplunkLogger from 'splunk-logging';

const logger = new SplunkLogger({
  token: process.env.SPLUNK_TOKEN,
  url: process.env.SPLUNK_URL
});

logger.send({
  message: auditEvent,
  severity: 'info',
  source: 'omniehr-api'
});
AWS CloudWatch:
import { CloudWatchLogsClient } from '@aws-sdk/client-cloudwatch-logs';

const cloudwatch = new CloudWatchLogsClient();
// Send audit events to CloudWatch Logs
3

Set up alerts

Monitor for suspicious activity:
  • Failed authentication attempts (>5 in 5 minutes)
  • Access to PHI outside business hours
  • Bulk data exports
  • Privilege escalation attempts
  • Database schema changes
  • Unusual geographic access patterns
4

Retain logs per compliance requirements

HIPAA requires 6-year retention:
  • Archive to S3 Glacier after 90 days
  • Set lifecycle policies for automatic archival
  • Encrypt all archived logs
  • Test restoration procedures quarterly

6. Network Security

Firewall rules:
# Allow HTTPS from anywhere
sudo ufw allow 443/tcp

# Allow SSH from specific IP only
sudo ufw allow from 203.0.113.0/24 to any port 22

# Deny MongoDB from public internet
sudo ufw deny 27017/tcp

# Enable firewall
sudo ufw enable
VPC configuration (AWS example):
  • API servers in private subnets
  • MongoDB in isolated subnets (no internet access)
  • Load balancer in public subnets
  • NAT gateway for outbound API calls
  • VPC endpoints for AWS services

Monitoring & Observability

Application Monitoring

// Already implemented in server/src/app.js
app.get("/api/health", (_req, res) => {
  res.json({ 
    status: "ok", 
    timestamp: new Date().toISOString() 
  });
});

// Enhanced health check
app.get("/api/health/detailed", async (req, res) => {
  const health = {
    status: "ok",
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    mongodb: await checkMongoHealth(),
    memory: process.memoryUsage(),
    version: process.env.APP_VERSION
  };
  res.json(health);
});

Key Metrics to Monitor

MetricThresholdAlert
API response time (p95)>500msWarning
API response time (p99)>1000msCritical
Error rate>1%Warning
Error rate>5%Critical
MongoDB connection pool>80% usedWarning
Memory usage>85%Warning
Disk usage>80%Warning
Failed auth attempts>10/minSecurity alert
Concurrent usersBaseline +300%Capacity alert

Log Aggregation

Centralize logs from all servers:
Winston + CloudWatch
import winston from 'winston';
import CloudWatchTransport from 'winston-cloudwatch';

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console(),
    new CloudWatchTransport({
      logGroupName: '/aws/omniehr/api',
      logStreamName: `${process.env.INSTANCE_ID}`,
      awsRegion: 'us-east-1'
    })
  ]
});

logger.info('Patient created', { patientId, userId });

Backup & Disaster Recovery

Database Backups

1

Automated backups

MongoDB Atlas: Enable continuous backups (point-in-time recovery)Self-hosted:
# Daily backup script
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
mongodump --uri="mongodb://backup-user:password@localhost:27017/ehr" \
  --archive="/backup/ehr_$DATE.archive" \
  --gzip

# Encrypt backup
openssl enc -aes-256-cbc -salt -in "/backup/ehr_$DATE.archive" \
  -out "/backup/ehr_$DATE.archive.enc" \
  -k "$BACKUP_ENCRYPTION_KEY"

# Upload to S3
aws s3 cp "/backup/ehr_$DATE.archive.enc" \
  s3://omniehr-backups/mongodb/

# Clean up local files
rm "/backup/ehr_$DATE.archive"*
Schedule with cron: 0 2 * * * /opt/scripts/backup.sh
2

Test restoration

Quarterly DR drill:
# Download backup
aws s3 cp s3://omniehr-backups/mongodb/ehr_20260304.archive.enc /tmp/

# Decrypt
openssl enc -aes-256-cbc -d -in /tmp/ehr_20260304.archive.enc \
  -out /tmp/ehr_20260304.archive -k "$BACKUP_ENCRYPTION_KEY"

# Restore to test database
mongorestore --uri="mongodb://localhost:27017/ehr_test" \
  --archive=/tmp/ehr_20260304.archive --gzip

# Verify data integrity
# ...
3

Retention policy

  • Daily backups: 30 days
  • Weekly backups: 12 weeks
  • Monthly backups: 7 years (HIPAA requirement)
  • Implement with S3 lifecycle policies

Application Deployment Rollback

Always maintain ability to rollback:
# Tag releases
git tag -a v1.2.3 -m "Release v1.2.3"
git push origin v1.2.3

# Deploy specific version
git checkout v1.2.3
npm install
pm2 restart omniehr-api

# Or use PM2 ecosystem file
module.exports = {
  apps: [{
    name: 'omniehr-api',
    script: 'server/src/server.js',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production'
    }
  }]
};

Compliance & Certification

HIPAA Technical Safeguards Checklist

Implemented in code:
  • Access control (authentication & authorization)
  • Audit controls (comprehensive logging)
  • Integrity controls (input validation with Zod)
  • Transmission security (HTTPS/TLS)
  • Encryption at rest (AES-256-GCM for PHI fields)
Required operational controls:
  • Business Associate Agreements with all vendors
  • Risk assessment (annually)
  • Security policies and procedures
  • Workforce training (annually)
  • Incident response plan
  • Contingency plan (disaster recovery)
  • Access controls (least privilege)
  • Facility access controls
  • Workstation security
  • Device and media controls

Security Assessment

Before going live:
1

Vulnerability scanning

# Dependency vulnerabilities
npm audit
npm audit fix

# Container scanning (if using Docker)
docker scan omniehr-api:latest

# Web vulnerability scanning
# Use tools like OWASP ZAP, Burp Suite
2

Penetration testing

Hire professional pentesters to assess:
  • Authentication bypass
  • Authorization flaws
  • Injection vulnerabilities
  • PHI data leakage
  • Session management
  • API security
3

Code review

Security-focused code review:
  • Encryption implementation
  • Authentication logic
  • Authorization checks
  • Input validation
  • Error handling (no info disclosure)
  • Audit logging completeness
4

Compliance audit

Third-party HIPAA compliance audit

Post-Deployment

Day 1 Checklist

  • Verify all services are running
  • Test login and basic workflows
  • Confirm audit logs are being created
  • Check monitoring dashboards
  • Set up alerting contacts
  • Document deployment details
  • Share runbook with on-call team

Ongoing Operations

Weekly:
  • Review audit logs for anomalies
  • Check system performance metrics
  • Review and triage error reports
Monthly:
  • Review and rotate access credentials
  • Update dependencies (npm update)
  • Test backup restoration
  • Security patch assessment
Quarterly:
  • Disaster recovery drill
  • Security assessment
  • Access audit (remove former employees)
  • Review and update incident response plan
Annually:
  • HIPAA risk assessment
  • Penetration testing
  • Staff security training
  • Policy and procedure review
  • Certificate renewal (SSL/TLS)

Troubleshooting Production Issues

High CPU Usage

# Check Node.js process
pm2 monit

# CPU profiling
node --prof server/src/server.js

# Analyze profile
node --prof-process isolate-*.log
Common causes:
  • Inefficient database queries (missing indexes)
  • Encryption/decryption in tight loops
  • Memory leaks causing GC thrashing

Memory Leaks

# Check memory usage
pm2 monit

# Generate heap snapshot
node --heapsnapshot-signal=SIGUSR2 server/src/server.js

# Trigger snapshot
kill -SIGUSR2 <pid>

# Analyze with Chrome DevTools

Database Connection Issues

# Check MongoDB status
mongosh --eval "db.adminCommand('ping')"

# Check connection pool
mongosh --eval "db.serverStatus().connections"

# Review connection string
echo $MONGODB_URI

API Errors

# View logs
pm2 logs omniehr-api --lines 100

# Filter errors
pm2 logs omniehr-api --err

# Follow logs
pm2 logs omniehr-api --lines 0

Additional Resources

Environment Variables

Complete environment variable reference

Local Setup

Development environment setup

Security Architecture

HIPAA-aligned security controls

API Reference

FHIR R4 API documentation

For questions about production deployment, security best practices, or compliance requirements, consult with qualified healthcare IT security professionals and legal counsel.