AuditTrail Deployment Guide
Docker Compose Production Setup
The included docker-compose.yml defines three services: the FastAPI API, the Next.js frontend, and an nginx reverse proxy.
Build and Start
cd AuditTrailCodebase
# Set the secret key (required for production)
export AUDITTRAIL_SECRET_KEY=$(openssl rand -hex 32)
# Build and start all services
docker compose up -d --build
The stack exposes:
| Port | Service | Description |
|---|---|---|
| 80 | nginx | Public entry point (routes to API and frontend) |
| 8000 | api | FastAPI backend (not exposed publicly behind nginx) |
| 3000 | web | Next.js frontend (not exposed publicly behind nginx) |
In production, you should remove the direct port mappings for api and web so they are only reachable through nginx. Edit docker-compose.yml:
services:
api:
# Remove or comment out:
# ports:
# - "8000:8000"
web:
# Remove or comment out:
# ports:
# - "3000:3000"
Persistent Data
The API stores its SQLite database in a named Docker volume api-data, mapped to /app/data inside the container. This volume survives container rebuilds.
To inspect the volume:
docker volume inspect audittrailcodebase_api-data
Environment Variables for Production
Create a .env file in the project root or set these variables in your deployment environment:
# REQUIRED: Change from the default. Generate with: openssl rand -hex 32
AUDITTRAIL_SECRET_KEY=your-production-secret-key
# Database URL
# SQLite (default, fine for small deployments):
DATABASE_URL=sqlite+aiosqlite:///data/audittrail.db
# PostgreSQL (recommended for production):
# DATABASE_URL=postgresql+asyncpg://user:password@db-host:5432/audittrail
# Disable debug mode
AUDITTRAIL_DEBUG=false
# Lock down CORS to your actual domain
AUDITTRAIL_CORS_ORIGINS=["https://your-domain.com"]
# PII redaction (enabled by default, keep it on)
AUDITTRAIL_PII_REDACTION_ENABLED=true
# Data retention
AUDITTRAIL_RETENTION_DAYS=90
# Frontend environment
NEXT_PUBLIC_API_URL=https://your-domain.com/api
NEXT_PUBLIC_WS_URL=wss://your-domain.com/ws
Nginx Reverse Proxy Configuration
The included nginx.conf handles routing, WebSocket upgrades, rate limiting, and security headers. Key features:
/api/and/ws/requests are proxied to the FastAPI backend- All other requests are proxied to the Next.js frontend
- WebSocket upgrade headers are set for
/ws/routes - Rate limiting: 10 requests/second per IP on API endpoints (burst of 20)
- Security headers: X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy
- Gzip compression enabled
- Long timeouts for ablation endpoints (300s) and WebSocket connections (3600s)
Adding TLS/SSL
For HTTPS, add a TLS server block. Example with Let's Encrypt certificates:
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# ... same location blocks as the existing server block ...
}
server {
listen 80;
server_name your-domain.com;
return 301 https://$host$request_uri;
}
Mount your certificates into the nginx container via a volume:
nginx:
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
Custom Domain
Update the NEXT_PUBLIC_API_URL and NEXT_PUBLIC_WS_URL environment variables for the web service to match your domain:
web:
environment:
- NEXT_PUBLIC_API_URL=https://your-domain.com/api
- NEXT_PUBLIC_WS_URL=wss://your-domain.com/ws
SQLite WAL Mode Considerations
SQLite is the default database for AuditTrail. In production with SQLite, note the following:
WAL (Write-Ahead Logging) mode is recommended for better concurrent read performance. SQLite enables WAL mode automatically with aiosqlite, but you can verify it:
sqlite3 data/audittrail.db "PRAGMA journal_mode;"
If it does not return wal, enable it:
sqlite3 data/audittrail.db "PRAGMA journal_mode=WAL;"
Limitations of SQLite in production:
- File-level write locking -- only one writer at a time. Under heavy ingest load, writes queue up.
- No connection pooling -- each request opens/closes the file.
- Database file must be on a local filesystem (not NFS or network shares).
- Maximum recommended concurrent users: ~10-20 for an observability dashboard.
When to switch to PostgreSQL: If you experience write contention (slow ingest under load), need concurrent multi-user access, or want JSONB queries for metadata search, set DATABASE_URL to a PostgreSQL connection string:
DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/audittrail
No code changes are required -- SQLAlchemy abstracts the database layer. Run Alembic migrations after switching:
cd apps/api
alembic upgrade head
Data Backup
SQLite Backup
Use the SQLite .backup command for a consistent snapshot (safe even while the server is running):
sqlite3 data/audittrail.db ".backup data/audittrail_backup_$(date +%Y%m%d).db"
For Docker deployments, exec into the container:
docker compose exec api sqlite3 /app/data/audittrail.db \
".backup /app/data/audittrail_backup.db"
Then copy the backup out:
docker compose cp api:/app/data/audittrail_backup.db ./backups/
Automated Backup Script
#!/bin/bash
# backup.sh -- Run via cron: 0 2 * * * /path/to/backup.sh
BACKUP_DIR="/path/to/backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
docker compose exec -T api sqlite3 /app/data/audittrail.db \
".backup /app/data/backup_${DATE}.db"
docker compose cp "api:/app/data/backup_${DATE}.db" "$BACKUP_DIR/"
# Remove backups older than 30 days
find "$BACKUP_DIR" -name "backup_*.db" -mtime +30 -delete
Generated Reports
PDF reports are stored at data/reports/ inside the API container. Back these up separately if needed:
docker compose cp api:/app/data/reports/ ./backups/reports/
Monitoring
Health Endpoint Polling
The API exposes GET /api/v1/health for health checks. The Docker Compose file already configures a health check that polls this endpoint every 30 seconds.
For external monitoring, poll the health endpoint and alert on:
statusis not"ok"(database connectivity issue)db_connectedisfalserules_loadedis0(rule directory misconfigured)- Endpoint is unreachable (service down)
Example with curl:
curl -sf http://localhost:8000/api/v1/health | python3 -c "
import json, sys
data = json.load(sys.stdin)
if data['status'] != 'ok':
print(f'ALERT: AuditTrail health degraded: {data}')
sys.exit(1)
print('OK')
"
Instance Info
GET /api/v1/instance provides additional metrics useful for dashboards:
curl http://localhost:8000/api/v1/instance
Returns version, connected agent count, rule count, and uptime.
Container Logs
# All services
docker compose logs -f
# API only
docker compose logs -f api
# Last 100 lines
docker compose logs --tail=100 api
Security Checklist
Before deploying to production, verify each item:
Authentication and Secrets
- Change the secret key. The default
dev-secret-key-change-in-productionis not safe. Generate a new one:openssl rand -hex 32 - Set
AUDITTRAIL_DEBUG=false. Debug mode exposes verbose error details.
Network Security
- Configure CORS origins. Set
AUDITTRAIL_CORS_ORIGINSto your actual domain(s) only. Do not use["*"]in production. - Enable TLS/SSL. Serve all traffic over HTTPS. WebSocket connections should use
wss://. - Remove direct port exposure. Remove the
portsmappings forapiandwebservices so they are only accessible through nginx. - Review nginx rate limiting. The default is 10 req/s per IP with burst of 20. Adjust for your traffic patterns.
Data Protection
- Enable PII redaction. Set
AUDITTRAIL_PII_REDACTION_ENABLED=true(enabled by default). This redacts personally identifiable information from ingested trace data. - Configure data retention. Set
AUDITTRAIL_RETENTION_DAYSto comply with your data retention policy. - Secure the database file. If using SQLite, ensure the
data/directory has restrictive file permissions. The Dockerfile creates a dedicatedaudittrailuser for this purpose. - Protect API keys. The secret key portion (
sk-at-...) is shown only once at creation time. It is stored as an Argon2 hash.
Constitutional Rules
- Review default rules. The
rules/directory ships with 6 default rules (bulk delete prevention, cost guard, latency SLO, PII guard, token budget, cite sources). Review and customize for your use case. - Mount rules as read-only. The Docker Compose file mounts
./rules:/app/rules:ro-- keep the:roflag to prevent the application from modifying rule files.
Backup
- Set up automated backups. See the Data Backup section above.
- Test restore procedure. Verify you can restore from a backup by copying it back and restarting the service.