Self-Hosting Guide
Deploy Agent OTP on your own infrastructure. Full control over your data, compliance with regulations, and air-gapped deployments.
Prerequisites
- Docker and Docker Compose
- PostgreSQL 15+ (or use included container)
- Redis 7+ (or use included container)
- Domain with SSL certificate (for production)
Quick Start with Docker
# Clone the repository
git clone https://github.com/orristech/agent-otp.git
cd agent-otp
# Copy environment template
cp .env.example .env
# Edit environment variables
nano .env
# Start all services
docker compose up -d
# Check status
docker compose psEnvironment Configuration
# .env file
# Database
DATABASE_URL=postgresql://postgres:password@db:5432/agent_otp
POSTGRES_PASSWORD=your-secure-password
# Redis
REDIS_URL=redis://redis:6379
# API Configuration
API_PORT=3000
API_SECRET_KEY=your-32-character-secret-key
JWT_SECRET=your-jwt-secret
# Dashboard
DASHBOARD_URL=https://otp.your-company.com
NEXT_PUBLIC_API_URL=https://api.otp.your-company.com
# Telegram Bot (optional)
TELEGRAM_BOT_TOKEN=your-bot-token
# Email Notifications (optional)
SMTP_HOST=smtp.your-company.com
SMTP_PORT=587
SMTP_USER=notifications@your-company.com
SMTP_PASSWORD=your-smtp-password
EMAIL_FROM=notifications@your-company.com
# Encryption
ENCRYPTION_KEY=your-32-byte-encryption-keyDocker Compose Configuration
# docker-compose.yml
version: '3.8'
services:
api:
image: ghcr.io/orristech/agent-otp-api:latest
ports:
- "3000:3000"
environment:
- DATABASE_URL=${DATABASE_URL}
- REDIS_URL=${REDIS_URL}
- API_SECRET_KEY=${API_SECRET_KEY}
- JWT_SECRET=${JWT_SECRET}
depends_on:
- db
- redis
restart: unless-stopped
dashboard:
image: ghcr.io/orristech/agent-otp-dashboard:latest
ports:
- "3001:3000"
environment:
- NEXT_PUBLIC_API_URL=${API_URL}
restart: unless-stopped
telegram-bot:
image: ghcr.io/orristech/agent-otp-telegram:latest
environment:
- API_URL=http://api:3000
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
depends_on:
- api
restart: unless-stopped
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=agent_otp
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
command: redis-server --appendonly yes
restart: unless-stopped
volumes:
postgres_data:
redis_data:Database Setup
Run migrations after starting the database:
# Apply database migrations
docker compose exec api bun run db:migrate
# Seed initial data (optional)
docker compose exec api bun run db:seedKubernetes Deployment
For production Kubernetes deployments:
# Add the Helm repository
helm repo add agent-otp https://charts.agentotp.com
helm repo update
# Create namespace
kubectl create namespace agent-otp
# Create secrets
kubectl create secret generic agent-otp-secrets \
--namespace agent-otp \
--from-literal=database-url='postgresql://...' \
--from-literal=redis-url='redis://...' \
--from-literal=api-secret='...'
# Install with Helm
helm install agent-otp agent-otp/agent-otp \
--namespace agent-otp \
--values values.yamlHelm Values Example
# values.yaml
api:
replicas: 3
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
dashboard:
replicas: 2
ingress:
enabled: true
host: otp.your-company.com
tls: true
postgresql:
enabled: false # Use external database
redis:
enabled: true
cluster:
enabled: true
ingress:
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prodSSL/TLS Configuration
With Let's Encrypt (Caddy)
# Caddyfile
api.otp.your-company.com {
reverse_proxy api:3000
}
otp.your-company.com {
reverse_proxy dashboard:3000
}With nginx
server {
listen 443 ssl http2;
server_name api.otp.your-company.com;
ssl_certificate /etc/letsencrypt/live/api.otp.your-company.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.otp.your-company.com/privkey.pem;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}High Availability
For production deployments requiring high availability:
- API - Deploy 3+ instances behind a load balancer
- PostgreSQL - Use a managed service or replica set
- Redis - Use Redis Cluster or Sentinel
- Telegram Bot - Single instance with failover
Backup and Recovery
# Backup database
docker compose exec db pg_dump -U postgres agent_otp > backup.sql
# Restore database
docker compose exec -T db psql -U postgres agent_otp < backup.sql
# Backup Redis
docker compose exec redis redis-cli BGSAVE
# Automated backup script
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
docker compose exec -T db pg_dump -U postgres agent_otp | gzip > "backup_${DATE}.sql.gz"
aws s3 cp "backup_${DATE}.sql.gz" s3://your-backup-bucket/Monitoring
The API exposes Prometheus metrics:
# Metrics endpoint
curl http://localhost:3000/metrics
# Example metrics
agent_otp_permissions_total{status="approved"} 1234
agent_otp_permissions_total{status="denied"} 56
agent_otp_token_usage_total 1180
agent_otp_api_latency_seconds{quantile="0.99"} 0.045Grafana Dashboard
Import the official Grafana dashboard:
# Dashboard ID: 12345
# Or import from: https://grafana.com/grafana/dashboards/12345Security Hardening
- Network isolation - Put database and Redis on private network
- Secrets management - Use Vault, AWS Secrets Manager, or similar
- Audit logging - Forward logs to SIEM system
- Encryption at rest - Enable database encryption
- Rate limiting - Configure per-IP rate limits
Updating
# Pull latest images
docker compose pull
# Restart services with zero downtime
docker compose up -d --no-deps api
docker compose up -d --no-deps dashboard
# Run any new migrations
docker compose exec api bun run db:migrate