Agent instructions, skills, SSH host aliases and expansion triggers are worth having identical on every machine one person owns, and belong in none of the shared configuration. They live in user/ now, with a manifest saying where each piece goes and a link-user stage that puts it there. That stage does nothing unless the machine said yes. Somebody who clones Panama to try the desktop keeps their own ~/.claude/CLAUDE.md exactly where it was; the question names the destinations and defaults to no. Anything displaced goes to config/old rather than being deleted. ~/.claude/CLAUDE.md and ~/.codex/AGENTS.md were byte-identical copies of one file, which is the drift this exists to prevent. Also adds the vitals toggles for the battery and Claude usage readouts, which had preferences and no way to reach them.
553 lines
13 KiB
Markdown
553 lines
13 KiB
Markdown
# Scaling and High Availability Guide
|
|
|
|
Infisical is a stateless application designed to scale horizontally. This guide covers scaling patterns, sizing recommendations, high availability (HA) setup, and upgrade procedures.
|
|
|
|
## Architecture Overview
|
|
|
|
Infisical's stateless architecture means:
|
|
|
|
- **All state is external**: PostgreSQL stores data, Redis handles caching and job queues
|
|
- **Horizontal scaling**: Add more Infisical replicas without reconfiguration
|
|
- **Load balancing**: Multiple replicas distribute traffic evenly
|
|
- **Zero shared state**: Each replica is identical and independent
|
|
|
|
This enables seamless scaling from single-node deployments to large distributed clusters.
|
|
|
|
## Sizing Recommendations
|
|
|
|
Choose deployment sizes based on your organization's users, secrets, and API request volume.
|
|
|
|
### Small Deployment
|
|
|
|
**Use Case**: Development, testing, small organizations (< 50 users)
|
|
|
|
**Infisical**:
|
|
- Replicas: 2
|
|
- CPU: 2 cores per replica
|
|
- Memory: 4-8 GB per replica
|
|
- Storage: N/A (stateless)
|
|
|
|
**PostgreSQL**:
|
|
- vCPU: 2
|
|
- Memory: 8 GB
|
|
- Storage: 100 GB (SSD recommended)
|
|
- Configuration: Single instance with automated backups
|
|
|
|
**Redis**:
|
|
- vCPU: 2
|
|
- Memory: 4 GB
|
|
- Storage: N/A (in-memory)
|
|
- Configuration: Standalone (simplest for this size)
|
|
|
|
### Medium Deployment
|
|
|
|
**Use Case**: Production environments (50-500 users)
|
|
|
|
**Infisical**:
|
|
- Replicas: 5
|
|
- CPU: 2-4 cores per replica
|
|
- Memory: 4-8 GB per replica
|
|
- Storage: N/A (stateless)
|
|
|
|
**PostgreSQL**:
|
|
- vCPU: 4
|
|
- Memory: 16 GB
|
|
- Storage: 200 GB (SSD)
|
|
- Configuration: Primary + read replicas for load distribution
|
|
|
|
**Redis**:
|
|
- vCPU: 2
|
|
- Memory: 4 GB
|
|
- Configuration: Standalone or Sentinel for HA
|
|
|
|
### Large Deployment
|
|
|
|
**Use Case**: Enterprise environments (500+ users)
|
|
|
|
**Infisical**:
|
|
- Replicas: 10+
|
|
- CPU: 2-4 cores per replica
|
|
- Memory: 4-8 GB per replica
|
|
- Storage: N/A (stateless)
|
|
|
|
**PostgreSQL**:
|
|
- vCPU: 8
|
|
- Memory: 32 GB
|
|
- Storage: 500 GB+ (SSD with RAID)
|
|
- Configuration: Primary + multiple read replicas, automated backups and WAL archiving
|
|
|
|
**Redis**:
|
|
- vCPU: 2-4
|
|
- Memory: 4-8 GB
|
|
- Configuration: Redis Sentinel for HA or Redis Cluster (but Infisical does NOT support Cluster mode)
|
|
|
|
## Horizontal Scaling
|
|
|
|
### Docker Compose
|
|
|
|
To scale Infisical with Docker Compose, create multiple service definitions:
|
|
|
|
```yaml
|
|
version: '3.8'
|
|
|
|
services:
|
|
postgres:
|
|
image: postgres:14-alpine
|
|
environment:
|
|
POSTGRES_USER: infisical
|
|
POSTGRES_PASSWORD: password
|
|
POSTGRES_DB: infisical
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
|
|
redis:
|
|
image: redis:7-alpine
|
|
volumes:
|
|
- redis_data:/data
|
|
|
|
infisical-1:
|
|
image: infisical/infisical:latest
|
|
depends_on:
|
|
- postgres
|
|
- redis
|
|
environment:
|
|
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
|
|
AUTH_SECRET: ${AUTH_SECRET}
|
|
DB_CONNECTION_URI: postgresql://infisical:password@postgres:5432/infisical
|
|
REDIS_URL: redis://redis:6379
|
|
SITE_URL: https://secrets.example.com
|
|
ports:
|
|
- "8001:8080"
|
|
|
|
infisical-2:
|
|
image: infisical/infisical:latest
|
|
depends_on:
|
|
- postgres
|
|
- redis
|
|
environment:
|
|
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
|
|
AUTH_SECRET: ${AUTH_SECRET}
|
|
DB_CONNECTION_URI: postgresql://infisical:password@postgres:5432/infisical
|
|
REDIS_URL: redis://redis:6379
|
|
SITE_URL: https://secrets.example.com
|
|
ports:
|
|
- "8002:8080"
|
|
|
|
infisical-3:
|
|
image: infisical/infisical:latest
|
|
depends_on:
|
|
- postgres
|
|
- redis
|
|
environment:
|
|
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
|
|
AUTH_SECRET: ${AUTH_SECRET}
|
|
DB_CONNECTION_URI: postgresql://infisical:password@postgres:5432/infisical
|
|
REDIS_URL: redis://redis:6379
|
|
SITE_URL: https://secrets.example.com
|
|
ports:
|
|
- "8003:8080"
|
|
|
|
nginx:
|
|
image: nginx:latest
|
|
volumes:
|
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
|
ports:
|
|
- "80:80"
|
|
depends_on:
|
|
- infisical-1
|
|
- infisical-2
|
|
- infisical-3
|
|
|
|
volumes:
|
|
postgres_data:
|
|
redis_data:
|
|
```
|
|
|
|
Use Nginx or HAProxy as a load balancer to distribute traffic:
|
|
|
|
```nginx
|
|
upstream infisical {
|
|
server infisical-1:8080;
|
|
server infisical-2:8080;
|
|
server infisical-3:8080;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
location / {
|
|
proxy_pass http://infisical;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Kubernetes
|
|
|
|
Scale using kubectl:
|
|
|
|
```bash
|
|
kubectl scale deployment infisical --replicas=10 -n infisical
|
|
```
|
|
|
|
Or update the Helm values:
|
|
|
|
```yaml
|
|
replicaCount: 10
|
|
```
|
|
|
|
Then apply:
|
|
|
|
```bash
|
|
helm upgrade infisical infisical/infisical-standalone-postgres \
|
|
--namespace infisical \
|
|
-f values.yaml
|
|
```
|
|
|
|
## Database Replication
|
|
|
|
### PostgreSQL Read Replicas
|
|
|
|
For large deployments, use PostgreSQL read replicas to distribute read-heavy queries (secrets, audit logs):
|
|
|
|
```bash
|
|
DB_READ_REPLICAS='[
|
|
{"connectionString": "postgresql://user:[email protected]:5432/infisical"},
|
|
{"connectionString": "postgresql://user:[email protected]:5432/infisical"}
|
|
]'
|
|
```
|
|
|
|
Infisical will distribute SELECT queries across replicas while ensuring writes go to the primary.
|
|
|
|
### Setting Up AWS RDS Read Replicas
|
|
|
|
1. Create a read replica in AWS RDS:
|
|
```bash
|
|
aws rds create-db-instance-read-replica \
|
|
--db-instance-identifier infisical-replica-1 \
|
|
--source-db-instance-identifier infisical-primary
|
|
```
|
|
|
|
2. Configure in Infisical:
|
|
```bash
|
|
DB_READ_REPLICAS='[
|
|
{"connectionString": "postgresql://user:password@infisical-replica-1.123456789.us-east-1.rds.amazonaws.com:5432/infisical"}
|
|
]'
|
|
```
|
|
|
|
## Redis High Availability
|
|
|
|
Infisical supports Redis Sentinel for high availability. Cluster mode is NOT supported.
|
|
|
|
### Redis Sentinel Setup
|
|
|
|
Sentinel monitors Redis and automatically promotes a replica to master if the primary fails.
|
|
|
|
#### Configure Sentinel
|
|
|
|
Create `sentinel.conf`:
|
|
|
|
```
|
|
port 26379
|
|
sentinel monitor mymaster 192.168.1.100 6379 2
|
|
sentinel down-after-milliseconds mymaster 5000
|
|
sentinel parallel-syncs mymaster 1
|
|
sentinel failover-timeout mymaster 180000
|
|
```
|
|
|
|
Run three Sentinel nodes for quorum:
|
|
|
|
```bash
|
|
redis-sentinel sentinel-1.conf
|
|
redis-sentinel sentinel-2.conf
|
|
redis-sentinel sentinel-3.conf
|
|
```
|
|
|
|
#### Configure Infisical to Use Sentinel
|
|
|
|
```bash
|
|
REDIS_SENTINEL_HOSTS="sentinel1.example.com:26379,sentinel2.example.com:26379,sentinel3.example.com:26379"
|
|
REDIS_SENTINEL_MASTER_NAME="mymaster"
|
|
REDIS_SENTINEL_ENABLE_TLS="true"
|
|
REDIS_SENTINEL_USERNAME="sentinel-user"
|
|
REDIS_SENTINEL_PASSWORD="sentinel-password"
|
|
```
|
|
|
|
Infisical will discover the current Redis master through Sentinel and automatically handle failovers.
|
|
|
|
### Docker Compose with Sentinel
|
|
|
|
```yaml
|
|
redis-master:
|
|
image: redis:7-alpine
|
|
ports:
|
|
- "6379:6379"
|
|
|
|
redis-replica:
|
|
image: redis:7-alpine
|
|
command: redis-server --slaveof redis-master 6379
|
|
depends_on:
|
|
- redis-master
|
|
|
|
sentinel-1:
|
|
image: redis:7-alpine
|
|
command: redis-sentinel /sentinel.conf
|
|
volumes:
|
|
- ./sentinel.conf:/sentinel.conf
|
|
ports:
|
|
- "26379:26379"
|
|
|
|
sentinel-2:
|
|
image: redis:7-alpine
|
|
command: redis-sentinel /sentinel.conf
|
|
volumes:
|
|
- ./sentinel.conf:/sentinel.conf
|
|
ports:
|
|
- "26380:26379"
|
|
|
|
sentinel-3:
|
|
image: redis:7-alpine
|
|
command: redis-sentinel /sentinel.conf
|
|
volumes:
|
|
- ./sentinel.conf:/sentinel.conf
|
|
ports:
|
|
- "26381:26379"
|
|
|
|
infisical:
|
|
image: infisical/infisical:latest
|
|
environment:
|
|
REDIS_SENTINEL_HOSTS: "sentinel-1:26379,sentinel-2:26379,sentinel-3:26379"
|
|
REDIS_SENTINEL_MASTER_NAME: "mymaster"
|
|
```
|
|
|
|
## Backup and Disaster Recovery
|
|
|
|
### PostgreSQL Backup Strategy
|
|
|
|
**Automated Backups**:
|
|
|
|
For managed PostgreSQL (RDS, Cloud SQL), use automated backups:
|
|
|
|
```bash
|
|
# AWS RDS
|
|
aws rds create-db-snapshot \
|
|
--db-instance-identifier infisical \
|
|
--db-snapshot-identifier infisical-backup-$(date +%s)
|
|
```
|
|
|
|
**Manual Backups**:
|
|
|
|
For self-hosted PostgreSQL:
|
|
|
|
```bash
|
|
pg_dump -h pg.example.com -U infisical infisical | gzip > backup_$(date +%Y%m%d).sql.gz
|
|
```
|
|
|
|
**Point-in-Time Recovery** (if WAL archiving is configured):
|
|
|
|
```bash
|
|
# Configure WAL archiving in PostgreSQL postgresql.conf
|
|
archive_mode = on
|
|
archive_command = 'aws s3 cp %p s3://backup-bucket/wal_archive/%f'
|
|
```
|
|
|
|
Then restore to a specific point in time.
|
|
|
|
### Redis Backup
|
|
|
|
Redis backups are less critical than database backups (data can be repopulated from PostgreSQL), but they can speed up recovery:
|
|
|
|
```bash
|
|
redis-cli BGSAVE
|
|
redis-cli LASTSAVE # Shows timestamp of last snapshot
|
|
```
|
|
|
|
Backup the RDB file periodically:
|
|
|
|
```bash
|
|
cp /var/lib/redis/dump.rdb /backup/redis_$(date +%s).rdb
|
|
```
|
|
|
|
### Backup Schedule
|
|
|
|
- **PostgreSQL**: Daily automated backups + continuous WAL archiving
|
|
- **Redis**: Daily snapshots (optional, less critical)
|
|
- **Retention**: Keep at least 30 days of backups for compliance
|
|
|
|
### Test Restores
|
|
|
|
Regularly test restores in a non-production environment to ensure backup integrity.
|
|
|
|
## Upgrades
|
|
|
|
Upgrading Infisical with zero downtime:
|
|
|
|
1. **Backup the database** (critical):
|
|
```bash
|
|
pg_dump -h pg.example.com -U infisical infisical > backup.sql
|
|
```
|
|
|
|
2. **Check the upgrade path** (optional):
|
|
Visit https://app.infisical.com/upgrade-path to verify your upgrade path and any special steps.
|
|
|
|
3. **Update replicas incrementally**:
|
|
|
|
For Kubernetes with rolling updates:
|
|
```bash
|
|
kubectl set image deployment/infisical infisical=infisical/infisical:new-version -n infisical
|
|
```
|
|
|
|
The rolling update ensures some replicas stay running while others update.
|
|
|
|
4. **Monitor the rollout**:
|
|
```bash
|
|
kubectl rollout status deployment/infisical -n infisical
|
|
```
|
|
|
|
5. **Schema migrations run automatically** on startup (since v0.111.0-postgres):
|
|
- One instance acquires a lock
|
|
- Migrations run
|
|
- Other instances wait for migrations to complete
|
|
- Cluster is ready
|
|
|
|
6. **Rollback if needed**:
|
|
```bash
|
|
kubectl rollout undo deployment/infisical -n infisical
|
|
```
|
|
|
|
## Licensing and Compliance
|
|
|
|
### License Server IP Addresses
|
|
|
|
Enterprise Infisical installations require connectivity to the license server. Whitelist these IPs in your firewall:
|
|
|
|
- `13.248.249.247`
|
|
- `35.71.190.59`
|
|
|
|
Ensure outbound HTTPS (port 443) is allowed to these addresses.
|
|
|
|
## Monitoring and Observability
|
|
|
|
### Key Metrics to Monitor
|
|
|
|
- **CPU and Memory**: Per-replica resource utilization
|
|
- **Request Latency**: API response times
|
|
- **Error Rate**: 5xx errors, database connection errors
|
|
- **Database Connections**: Active connections, connection pool saturation
|
|
- **Redis Memory**: Memory usage and eviction
|
|
- **Database Query Time**: Slow query logs
|
|
|
|
### Prometheus Metrics
|
|
|
|
Infisical exposes metrics on `/metrics` (OpenTelemetry format):
|
|
|
|
```bash
|
|
curl http://infisical:8080/metrics
|
|
```
|
|
|
|
### Log Aggregation
|
|
|
|
Aggregate logs from all replicas using your logging platform:
|
|
|
|
```bash
|
|
# Docker Compose
|
|
docker-compose logs infisical | grep ERROR
|
|
|
|
# Kubernetes
|
|
kubectl logs -l app=infisical -n infisical --all-containers=true
|
|
```
|
|
|
|
## Performance Tuning
|
|
|
|
### PostgreSQL Tuning
|
|
|
|
For large deployments, optimize PostgreSQL:
|
|
|
|
```sql
|
|
-- Increase shared buffers (typically 25% of RAM)
|
|
ALTER SYSTEM SET shared_buffers = '8GB';
|
|
|
|
-- Increase effective cache size (typically 50-75% of RAM)
|
|
ALTER SYSTEM SET effective_cache_size = '24GB';
|
|
|
|
-- Increase work_mem for complex queries
|
|
ALTER SYSTEM SET work_mem = '8MB';
|
|
|
|
-- Reload configuration
|
|
SELECT pg_reload_conf();
|
|
```
|
|
|
|
### Redis Tuning
|
|
|
|
Increase maxmemory if needed:
|
|
|
|
```bash
|
|
redis-cli CONFIG SET maxmemory 4gb
|
|
redis-cli CONFIG SET maxmemory-policy allkeys-lru
|
|
```
|
|
|
|
### Connection Pool Tuning
|
|
|
|
Monitor connection pool saturation and adjust if needed:
|
|
|
|
```bash
|
|
# Check PostgreSQL max connections
|
|
psql -h pg.example.com -U infisical -d infisical -c "SHOW max_connections;"
|
|
```
|
|
|
|
Increase if you have many Infisical replicas:
|
|
|
|
```bash
|
|
ALTER SYSTEM SET max_connections = 400;
|
|
```
|
|
|
|
## Troubleshooting HA Setups
|
|
|
|
### Redis Sentinel Failover Not Triggering
|
|
|
|
Check Sentinel logs:
|
|
|
|
```bash
|
|
redis-cli -p 26379 SENTINEL MASTERS
|
|
redis-cli -p 26379 SENTINEL SLAVES mymaster
|
|
```
|
|
|
|
Ensure Sentinel nodes can communicate with Redis.
|
|
|
|
### Database Connection Pool Exhaustion
|
|
|
|
If you see "too many connections" errors:
|
|
|
|
1. Check current connections:
|
|
```bash
|
|
psql -c "SELECT datname, count(*) FROM pg_stat_activity GROUP BY datname;"
|
|
```
|
|
|
|
2. Increase PostgreSQL max_connections
|
|
3. Reduce Infisical replicas or increase connection pool size
|
|
|
|
### Read Replica Lag
|
|
|
|
If read replicas are lagging, monitor replication lag:
|
|
|
|
```bash
|
|
# AWS RDS
|
|
aws rds describe-db-instances --query 'DBInstances[*].[DBInstanceIdentifier,ReplicationLag]'
|
|
```
|
|
|
|
Lag > 1 second may cause stale reads. Infisical writes always use the primary.
|
|
|
|
## Capacity Planning
|
|
|
|
To estimate hardware needs:
|
|
|
|
- **Users**: 10 users per core for moderate activity
|
|
- **Secrets**: 1 million secrets per 10 GB of PostgreSQL storage
|
|
- **API Calls**: 1000 req/sec per 2 cores with 4 GB memory
|
|
- **Database Connections**: 20-50 per Infisical replica
|
|
|
|
Example for 200 users:
|
|
- Infisical: 3 replicas x 2 cores
|
|
- PostgreSQL: 4 vCPU, 16 GB RAM, 100 GB storage
|
|
- Redis: 2 vCPU, 4 GB RAM
|
|
|
|
Adjust based on actual monitoring and load testing.
|