Deployment Guide
This guide covers deploying Milvaion to production environments using Docker, Kubernetes, and traditional VMs.
Deployment Checklist
Before going to production, ensure you have:
- PostgreSQL with proper backup strategy
- Redis with persistence enabled (RDB or AOF)
- RabbitMQ with durable queues
- Health checks configured
- Resource limits defined
Docker Compose (Simple Production)
For single-server or small deployments:
docker-compose.prod.yml
version: '3.8'
services:
postgres:
image: postgres:16-alpine
container_name: milvaion-postgres
restart: always
environment:
POSTGRES_DB: MilvaionDb
POSTGRES_USER: milvaion
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./backups:/backups
healthcheck:
test: ["CMD-SHELL", "pg_isready -U milvaion -d MilvaionDb"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: milvaion-redis
restart: always
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"]
interval: 10s
timeout: 5s
retries: 5
rabbitmq:
image: rabbitmq:3-management-alpine
container_name: milvaion-rabbitmq
restart: always
environment:
RABBITMQ_DEFAULT_USER: milvaion
RABBITMQ_DEFAULT_PASS_FILE: /run/secrets/rabbitmq_password
secrets:
- rabbitmq_password
volumes:
- rabbitmq_data:/var/lib/rabbitmq
healthcheck:
test: ["CMD", "rabbitmqctl", "status"]
interval: 30s
timeout: 10s
retries: 5
milvaion-api:
image: milvasoft/milvaion-api:1.0.0
container_name: milvaion-api
restart: always
ports:
- "5000:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__DefaultConnectionString=Host=postgres;Port=5432;Database=MilvaionDb;Username=milvaion;Password=${DB_PASSWORD}
- MilvaionConfig__Redis__ConnectionString=redis:6379
- MilvaionConfig__Redis__Password=${REDIS_PASSWORD}
- MilvaionConfig__RabbitMQ__Host=rabbitmq
- MilvaionConfig__RabbitMQ__Username=milvaion
- MilvaionConfig__RabbitMQ__Password=${RABBITMQ_PASSWORD}
- MILVAION_ROOT_PASSWORD=admin
- MILVA_ENV=prod
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
rabbitmq:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
email-worker:
image: my-company/email-worker:1.0.0
restart: always
deploy:
replicas: 3
environment:
- Worker__WorkerId=email-worker
- Worker__MaxParallelJobs=20
- Worker__RabbitMQ__Host=rabbitmq
- Worker__RabbitMQ__Username=milvaion
- Worker__RabbitMQ__Password=${RABBITMQ_PASSWORD}
- Worker__Redis__ConnectionString=redis:6379,password=${REDIS_PASSWORD}
depends_on:
- milvaion-api
secrets:
db_password:
file: ./secrets/db_password.txt
rabbitmq_password:
file: ./secrets/rabbitmq_password.txt
volumes:
postgres_data:
redis_data:
rabbitmq_data:
Deploy Commands
# Create secrets
mkdir -p secrets
echo "your-secure-db-password" > secrets/db_password.txt
echo "your-secure-rabbitmq-password" > secrets/rabbitmq_password.txt
# Create .env file
cat > .env << EOF
DB_PASSWORD=your-secure-db-password
REDIS_PASSWORD=your-secure-redis-password
RABBITMQ_PASSWORD=your-secure-rabbitmq-password
EOF
# Deploy
docker compose -f docker-compose.prod.yml up -d
# Scale workers
docker compose -f docker-compose.prod.yml up -d --scale email-worker=5
Kubernetes Deployment
For larger, scalable deployments:
Namespace and Secrets
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: milvaion
---
# secrets.yaml
apiVersion: v1
kind: Secret
metadata:
name: milvaion-secrets
namespace: milvaion
type: Opaque
stringData:
db-password: "your-secure-db-password"
redis-password: "your-secure-redis-password"
rabbitmq-password: "your-secure-rabbitmq-password"
API Deployment
# api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: milvaion-api
namespace: milvaion
spec:
replicas: 2
selector:
matchLabels:
app: milvaion-api
template:
metadata:
labels:
app: milvaion-api
spec:
containers:
- name: api
image: milvasoft/milvaion-api:1.0.0
ports:
- containerPort: 8080
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
- name: ConnectionStrings__DefaultConnectionString
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: db-connection-string
- name: MilvaionConfig__Redis__ConnectionString
value: "redis.milvaion.svc:6379"
- name: MilvaionConfig__Redis__Password
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: redis-password
- name: MilvaionConfig__RabbitMQ__Host
value: "rabbitmq.milvaion.svc"
- name: MilvaionConfig__RabbitMQ__Password
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: rabbitmq-password
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: milvaion-api
namespace: milvaion
spec:
selector:
app: milvaion-api
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Worker Deployment
# worker-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: email-worker
namespace: milvaion
spec:
replicas: 3
selector:
matchLabels:
app: email-worker
template:
metadata:
labels:
app: email-worker
spec:
containers:
- name: worker
image: my-company/email-worker:1.0.0
env:
- name: Worker__WorkerId
value: "email-worker"
- name: Worker__MaxParallelJobs
value: "20"
- name: Worker__RabbitMQ__Host
value: "rabbitmq.milvaion.svc"
- name: Worker__RabbitMQ__Password
valueFrom:
secretKeyRef:
name: milvaion-secrets
key: rabbitmq-password
- name: Worker__Redis__ConnectionString
value: "redis.milvaion.svc:6379"
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
exec:
command: ["cat", "/tmp/healthy"]
initialDelaySeconds: 30
periodSeconds: 10
Horizontal Pod Autoscaler
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: email-worker-hpa
namespace: milvaion
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: email-worker
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Traditional Deployment (VM/Bare Metal)
For deployments on VMs, bare metal servers, or when not using containers.
Prerequisites:
- PostgreSQL, Redis, and RabbitMQ are already installed and running
- .NET 10 Runtime is installed (
dotnet --versionshows 10.x) - Appropriate network access between components
1. Deploy Milvaion API
Step 1: Build the API
On your build server or development machine:
# Clone repository
git clone https://github.com/Milvasoft/milvaion.git
cd milvaion
# Build in Release mode
dotnet publish src/Milvaion.Api/Milvaion.Api.csproj \
-c Release \
-o ./publish/api \
--self-contained false
# Create deployment package
cd publish/api
tar -czf milvaion-api.tar.gz *
Step 2: Transfer to Production Server
# Transfer to server
scp milvaion-api.tar.gz user@production-server:/opt/milvaion/
# On production server
cd /opt/milvaion
tar -xzf milvaion-api.tar.gz
rm milvaion-api.tar.gz
Step 3: Configure appsettings.Production.json
Create /opt/milvaion/appsettings.Production.json:
{
"ConnectionStrings": {
"DefaultConnectionString": "Host=postgres;Port=5432;Database=MilvaionDb;Username=postgres;Password=N4SQp.qW>6?xwWzg;Pooling=true;Minimum Pool Size=20;Maximum Pool Size=100;Connection Lifetime=900;Connection Idle Lifetime=180;Command Timeout=30;Include Error Detail=true"
},
"Cors": {
"DefaultPolicy": "AllowAll",
"Policies": {
"AllowAll": {
"AllowAnyOriginWithCredentials": true,
"Origins": [ "All" ],
"Methods": [ "All" ],
"Headers": [ "All" ],
"ExposedHeaders": [ "Content-Disposition" ],
"AllowCredentials": false
}
}
},
"MilvaionConfig": {
"Logging": {
"Seq": {
"Enabled": true,
"Uri": "http://seq:5341"
}
},
"OpenTelemetry": {
"Enabled": true,
"ExportPath": "/api/metrics",
"Service": "milvaion-backend",
"Environment": "milvaion-test",
"Job": "app-metrics",
"Instance": "milvaion-test"
},
"Redis": {
"ConnectionString": "redis:6379",
"Password": "",
"Database": 0,
"ConnectTimeout": 5000,
"SyncTimeout": 5000,
"KeyPrefix": "Milvaion:JobScheduler:",
"DefaultLockTtlSeconds": 600
},
"RabbitMQ": {
"Host": "rabbitmq",
"Port": 5672,
"Username": "guest",
"Password": "guest",
"VirtualHost": "/",
"Durable": true,
"AutoDelete": false,
"ConnectionTimeout": 30,
"Heartbeat": 60,
"AutomaticRecoveryEnabled": true,
"NetworkRecoveryInterval": 10,
"QueueDepthWarningThreshold": 100,
"QueueDepthCriticalThreshold": 500
},
"JobDispatcher": {
"Enabled": true,
"PollingIntervalSeconds": 1,
"BatchSize": 100,
"LockTtlSeconds": 600,
"EnableStartupRecovery": true
},
"WorkerAutoDiscovery": {
"Enabled": true
},
"ZombieOccurrenceDetector": {
"Enabled": true,
"CheckIntervalSeconds": 300,
"ZombieTimeoutMinutes": 10
},
"LogCollector": {
"Enabled": true,
"BatchSize": 100,
"BatchIntervalMs": 1000
},
"StatusTracker": {
"Enabled": true,
"BatchSize": 50,
"BatchIntervalMs": 100,
"ExecutionLogMaxCount": 100
},
"FailedOccurrenceHandler": {
"Enabled": true
},
"JobAutoDisable": {
"Enabled": true,
"ConsecutiveFailureThreshold": 5,
"FailureWindowMinutes": 60,
"AutoReEnableAfterCooldown": false,
"AutoReEnableCooldownMinutes": 30
}
},
"Serilog": {
"Using": [ "Serilog.Expressions", "Serilog.Sinks.Console" ],
"MinimumLevel": {
"Default": "Debug",
"Override": {
"Microsoft.AspNetCore": "Information",
"System": "Warning",
"Microsoft.AspNetCore.Mvc": "Warning",
"Microsoft.AspNetCore.Cors": "Warning",
"Microsoft.AspNetCore.Routing": "Warning",
"Microsoft.AspNetCore.Hosting.Diagnostics": "Warning",
"Microsoft.EntityFrameworkCore.Database.Command": "Warning",
"Microsoft.EntityFrameworkCore.Update": "Warning",
"Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler": "Warning"
}
},
"WriteTo": [
{
"Name": "Logger",
"Args": {
"configureLogger": {
"WriteTo": [
{
"Name": "Console"
}
]
}
}
}
]
}
}
Step 4: Apply Database Migrations
cd /opt/milvaion
# Apply migrations
dotnet Milvaion.Api.dll --migrate
# Or manually with EF Core tools
dotnet ef database update --project /path/to/Milvaion.Api.csproj
Step 5: Create systemd Service
Create /etc/systemd/system/milvaion-api.service:
[Unit]
Description=Milvaion Scheduler API
After=network.target postgresql.service redis.service rabbitmq-server.service
[Service]
Type=notify
User=milvaion
Group=milvaion
WorkingDirectory=/opt/milvaion
ExecStart=/usr/bin/dotnet /opt/milvaion/Milvaion.Api.dll
Restart=always
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=milvaion-api
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=ASPNETCORE_URLS=http://0.0.0.0:5000
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
# Resource limits
LimitNOFILE=65536
MemoryLimit=2G
[Install]
WantedBy=multi-user.target
Step 6: Start the Service
# Create user for running service
sudo useradd -r -s /bin/false milvaion
sudo chown -R milvaion:milvaion /opt/milvaion
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable milvaion-api
sudo systemctl start milvaion-api
# Check status
sudo systemctl status milvaion-api
# View logs
sudo journalctl -u milvaion-api -f
Step 7: Configure Reverse Proxy (Nginx)
Create /etc/nginx/sites-available/milvaion:
upstream milvaion_api {
server localhost:5000;
}
server {
listen 80;
server_name api.your-domain.com;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.your-domain.com;
ssl_certificate /etc/ssl/certs/your-cert.crt;
ssl_certificate_key /etc/ssl/private/your-key.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
client_max_body_size 10M;
location / {
proxy_pass http://milvaion_api;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection keep-alive;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Health check endpoint (no auth required)
location /api/v1/healthcheck {
proxy_pass http://milvaion_api;
access_log off;
}
}
Enable and reload Nginx:
sudo ln -s /etc/nginx/sites-available/milvaion /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Step 8: Verify Deployment
# Check health
curl http://localhost:5000/api/v1/healthcheck/ready
# Or via Nginx
curl https://api.your-domain.com/api/v1/healthcheck/ready
# Check logs
sudo journalctl -u milvaion-api --since "10 minutes ago"
2. Deploy Worker
Step 1: Build the Worker
# On build server
cd your-worker-project
# Build in Release mode
dotnet publish -c Release -o ./publish/worker --self-contained false
# Create deployment package
cd publish/worker
tar -czf my-worker.tar.gz *
Step 2: Transfer to Production Server
# Transfer to server (can be same or different server as API)
scp my-worker.tar.gz user@worker-server:/opt/milvaion-workers/my-worker/
# On worker server
cd /opt/milvaion-workers/my-worker
tar -xzf my-worker.tar.gz
rm my-worker.tar.gz
Step 3: Configure appsettings.Production.json
Create /opt/milvaion-workers/my-worker/appsettings.Production.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning"
}
},
"Serilog": {
"Using": ["Serilog.Sinks.Console", "Serilog.Sinks.Seq"],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog"
}
},
{
"Name": "Seq",
"Args": {
"serverUrl": "http://localhost:5341"
}
}
],
"Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"]
},
"Worker": {
"WorkerId": "my-worker-prod-01",
"MaxParallelJobs": 20,
"ExecutionTimeoutSeconds": 300,
"RabbitMQ": {
"Host": "localhost",
"Port": 5672,
"Username": "milvaion",
"Password": "YOUR_RABBITMQ_PASSWORD",
"VirtualHost": "/"
},
"Redis": {
"ConnectionString": "localhost:6379",
"Password": "YOUR_REDIS_PASSWORD",
"Database": 0,
"CancellationChannel": "Milvaion:JobScheduler:cancellation_channel"
},
"Heartbeat": {
"Enabled": true,
"IntervalSeconds": 5,
"JobHeartbeatIntervalSeconds": 60
},
"HealthCheck": {
"Enabled": true,
"LiveFilePath": "/tmp/milvaion-worker-live",
"ReadyFilePath": "/tmp/milvaion-worker-ready",
"IntervalSeconds": 30
},
"OfflineResilience": {
"Enabled": true,
"LocalStoragePath": "/var/lib/milvaion-worker/outbox",
"SyncIntervalSeconds": 30,
"MaxSyncRetries": 3,
"CleanupIntervalHours": 1,
"RecordRetentionDays": 1
}
},
"JobConsumers": {
"MyJob": {
"ConsumerId": "my-job-consumer",
"MaxParallelJobs": 30,
"MaxParallelJobsPerWorker": 30,
"ExecutionTimeoutSeconds": 120,
"MaxRetries": 3,
"BaseRetryDelaySeconds": 10,
"LogUserFriendlyLogsViaLogger": true
}
}
}
Step 4: Create systemd Service
Create /etc/systemd/system/milvaion-worker-my-worker.service:
[Unit]
Description=Milvaion Worker - My Worker
After=network.target rabbitmq-server.service redis.service
[Service]
Type=simple
User=milvaion
Group=milvaion
WorkingDirectory=/opt/milvaion-workers/my-worker
ExecStart=/usr/bin/dotnet /opt/milvaion-workers/my-worker/MyWorker.dll
Restart=always
RestartSec=10
KillSignal=SIGTERM
TimeoutStopSec=30
SyslogIdentifier=milvaion-worker-my-worker
Environment=DOTNET_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
# Resource limits
LimitNOFILE=65536
MemoryLimit=1G
[Install]
WantedBy=multi-user.target
Step 5: Start the Worker
# Create directories
sudo mkdir -p /var/lib/milvaion-worker/outbox
sudo chown -R milvaion:milvaion /opt/milvaion-workers/my-worker
sudo chown -R milvaion:milvaion /var/lib/milvaion-worker
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable milvaion-worker-my-worker
sudo systemctl start milvaion-worker-my-worker
# Check status
sudo systemctl status milvaion-worker-my-worker
# View logs
sudo journalctl -u milvaion-worker-my-worker -f
Step 6: Scale Workers (Optional)
For multiple worker instances:
# Copy worker to different directories
sudo cp -r /opt/milvaion-workers/my-worker /opt/milvaion-workers/my-worker-02
sudo cp -r /opt/milvaion-workers/my-worker /opt/milvaion-workers/my-worker-03
# Update WorkerId in each appsettings.Production.json
sudo nano /opt/milvaion-workers/my-worker-02/appsettings.Production.json
# Change: "WorkerId": "my-worker-prod-02"
# Create separate systemd services
sudo cp /etc/systemd/system/milvaion-worker-my-worker.service \
/etc/systemd/system/milvaion-worker-my-worker-02.service
# Update paths and start
sudo systemctl daemon-reload
sudo systemctl enable milvaion-worker-my-worker-02
sudo systemctl start milvaion-worker-my-worker-02
Step 7: Verify Worker Deployment
# Check worker is running
sudo systemctl status milvaion-worker-my-worker
# Check logs for connection
sudo journalctl -u milvaion-worker-my-worker | grep -E "Connected|Started"
# Check RabbitMQ Management UI
# http://localhost:15672 - should see worker connection
# Verify worker registration in API
curl https://api.your-domain.com/api/v1/workers \
-H "Authorization: Bearer YOUR_TOKEN"
# Check health file
ls -la /tmp/milvaion-worker-live
ls -la /tmp/milvaion-worker-ready
Monitoring and Maintenance
Log Rotation
Create /etc/logrotate.d/milvaion:
/var/log/milvaion/*.log {
daily
rotate 14
compress
delaycompress
notifempty
create 0640 milvaion milvaion
sharedscripts
postrotate
systemctl reload milvaion-api > /dev/null 2>&1 || true
endscript
}
Monitoring Script
Create /usr/local/bin/check-milvaion-health.sh:
#!/bin/bash
API_URL="http://localhost:5000/api/v1/healthcheck/ready"
WORKER_HEALTH="/tmp/milvaion-worker-live"
# Check API
if ! curl -f -s "$API_URL" > /dev/null; then
echo "API health check failed!"
systemctl restart milvaion-api
fi
# Check Worker
if [ ! -f "$WORKER_HEALTH" ]; then
echo "Worker health file missing!"
systemctl restart milvaion-worker-my-worker
fi
# Check file age (should be updated every 30s)
if [ $(( $(date +%s) - $(stat -c %Y "$WORKER_HEALTH") )) -gt 60 ]; then
echo "Worker health file is stale!"
systemctl restart milvaion-worker-my-worker
fi
Add to crontab:
sudo crontab -e
# Add line:
*/5 * * * * /usr/local/bin/check-milvaion-health.sh
Health Checks
See the health check section for detailed information.
Resource Recommendations
API Server
| Workload | CPU | Memory | Replicas |
|---|---|---|---|
| Small (<1K jobs/day) | 250m | 512Mi | 1 |
| Medium (1K-10K jobs/day) | 500m | 2Gi | 1 |
| Large (>10K jobs/day) | 1000m | 4Gi | 1 |
Workers
| Job Type | CPU | Memory | Concurrency |
|---|---|---|---|
| I/O-bound (email, API calls) | 100m | 128Mi | 50-100 |
| CPU-bound (reports, processing) | 500m | 512Mi | 2-5 |
| Memory-intensive (data analysis) | 250m | 2Gi | 1-2 |
Infrastructure
| Component | Production Recommendation |
|---|---|
| PostgreSQL | 2 CPU, 4GB RAM, SSD storage |
| Redis | 1 CPU, 2GB RAM, persistence enabled |
| RabbitMQ | 2 CPU, 2GB RAM, durable queues |
Logging in Workers
Structured Logging
Configure JSON logging for production:
{
"Serilog": {
"Using": ["Serilog.Sinks.Console"],
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "Console",
"Args": {
"formatter": "Serilog.Formatting.Json.JsonFormatter, Serilog"
}
}
],
"Enrich": ["FromLogContext", "WithMachineName", "WithThreadId"]
}
}
Centralized Logging
Send logs to ELK, Seq, or cloud logging:
// Program.cs
builder.Host.UseSerilog((context, config) =>
{
config
.ReadFrom.Configuration(context.Configuration)
.WriteTo.Seq("http://seq.internal:5341")
.Enrich.WithProperty("Service", "email-worker")
.Enrich.WithProperty("Environment", context.HostingEnvironment.EnvironmentName);
});
Backup Strategy
PostgreSQL
# Daily backup script
#!/bin/bash
BACKUP_DIR="/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
pg_dump -h localhost -U milvaion MilvaionDb | gzip > $BACKUP_DIR/milvaion_$TIMESTAMP.sql.gz
# Keep last 7 days
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
Redis
Enable persistence in redis.conf:
# RDB snapshots
save 900 1
save 300 10
save 60 10000
# AOF for durability
appendonly yes
appendfsync everysec
Security Checklist
- All passwords are in secrets, not config files
- TLS enabled for all external connections
- Database accessible only from API server
- Redis/RabbitMQ not exposed publicly
- API behind load balancer/reverse proxy
- Rate limiting on API endpoints
- Audit logging enabled
- Regular security updates
What's Next?
- Reliability - Retry, DLQ, and error handling
- Scaling - Horizontal scaling strategies
- Monitoring - Metrics and alerting