This guide provides a complete Docker Compose deployment for Infisical, including production-ready configurations, security hardening, TLS setup, and backup strategies.
Current Version: v0.158.5 (February 2026) | Docker Image: infisical/infisical:v0.158.5-postgres
Docker Compose is the recommended deployment method for:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Docker Network β
β β
β ββββββββββββββββ ββββββββββββββββββββββββββββββββββββ β
β β Nginx ββββββΆβ Infisical Backend β β
β β (Proxy) β β (Node.js) β β
β β Port 443 β β Port 8080 β β
β ββββββββββββββββ ββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββΌββββββββββββββββββ β
β βΌ βΌ βΌ β
β ββββββββββββ ββββββββββββ ββββββββββββββββ β
β β Redis β β Postgres β β Migration β β
β β (Cache) β β (DB) β β (Job) β β
β β :6379 β β :5432 β β (One-time) β β
β ββββββββββββ ββββββββββββ ββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 2 cores | 4+ cores |
| RAM | 2 GB | 4-8 GB |
| Disk | 10 GB SSD | 50+ GB SSD |
| OS | Linux (any distro) | Debian 12+, Ubuntu 22.04+ |
# Docker Engine 20.10+
docker --version
# Docker Compose Plugin 2.0+
docker compose version
Install Docker if not already installed:
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
mkdir -p ~/infisical/{nginx/certs,volumes/postgres,volumes/redis,backups}
cd ~/infisical
# Generate encryption key (32 hex characters)
ENCRYPTION_KEY=$(openssl rand -hex 16)
echo "ENCRYPTION_KEY=$ENCRYPTION_KEY"
# Generate auth secret (base64 encoded)
AUTH_SECRET=$(openssl rand -base64 32)
echo "AUTH_SECRET=$AUTH_SECRET"
# Generate strong database password
DB_PASSWORD=$(openssl rand -base64 24 | tr -d '=+')
echo "DB_PASSWORD=$DB_PASSWORD"
β οΈ Important: Save these values securely. The
ENCRYPTION_KEYis required for disaster recovery.
cat > .env << EOF
# ===========================================
# Infisical Environment Configuration
# ===========================================
# Site Configuration
SITE_URL=https://infisical.example.com
# Security Keys (REQUIRED - replace with generated values)
ENCRYPTION_KEY=${ENCRYPTION_KEY}
AUTH_SECRET=${AUTH_SECRET}
# PostgreSQL Configuration
POSTGRES_USER=infisical
POSTGRES_PASSWORD=${DB_PASSWORD}
POSTGRES_DB=infisical
DB_CONNECTION_URI=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
# Redis Configuration
REDIS_URL=redis://redis:6379
# JWT Token Lifetimes (optional, defaults shown)
# JWT_AUTH_LIFETIME=15m
# JWT_REFRESH_LIFETIME=24h
# JWT_SERVICE_LIFETIME=1h
# Optional: SMTP Configuration for Email Notifications
# SMTP_HOST=smtp.example.com
# SMTP_PORT=587
# SMTP_USERNAME=noreply@example.com
# SMTP_PASSWORD=your-smtp-password
# SMTP_FROM_ADDRESS=noreply@example.com
# SMTP_FROM_NAME=Infisical
# SMTP_REQUIRE_TLS=true
# Optional: Telemetry (disabled by default)
# TELEMETRY_ENABLED=false
# Optional: CORS Configuration
# CORS_ALLOWED_ORIGINS=["https://app.example.com"]
EOF
cat > docker-compose.yml << 'EOF'
services:
# ===========================================
# Infisical Backend Application
# ===========================================
backend:
image: infisical/infisical:v0.93.1-postgres
container_name: infisical-backend
restart: unless-stopped
pull_policy: always
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
db-migration:
condition: service_completed_successfully
env_file: .env
environment:
- NODE_ENV=production
ports:
- "127.0.0.1:8080:8080"
networks:
- infisical
volumes:
- ./backups:/app/backups
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/status"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# Security hardening
read_only: true
tmpfs:
- /tmp:rw,exec,size=1G
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
# ===========================================
# Redis Cache
# ===========================================
redis:
image: redis:7-alpine
container_name: infisical-redis
restart: unless-stopped
env_file: .env
command: redis-server --appendonly yes
volumes:
- ./volumes/redis:/data
networks:
- infisical
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# Security hardening
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
# ===========================================
# PostgreSQL Database
# ===========================================
db:
image: postgres:14-alpine
container_name: infisical-db
restart: unless-stopped
env_file: .env
volumes:
- ./volumes/postgres:/var/lib/postgresql/data
- ./backups/postgres:/backups
networks:
- infisical
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
# Security hardening
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
security_opt:
- no-new-privileges:true
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
# ===========================================
# Database Migration (One-time job)
# ===========================================
db-migration:
image: infisical/infisical:v0.93.1-postgres
container_name: infisical-db-migration
depends_on:
db:
condition: service_healthy
env_file: .env
command: npm run migration:latest
networks:
- infisical
# Security hardening
read_only: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
networks:
infisical:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16
EOF
openssl req -subj '/CN=infisical.example.com' \
-addext "subjectAltName = DNS:infisical.example.com,DNS:localhost" \
-x509 -newkey rsa:4096 -nodes \
-keyout nginx/certs/key.pem \
-out nginx/certs/cert.pem \
-days 365
# Install Certbot
sudo apt-get install -y certbot
# Obtain certificate (standalone mode - requires port 80)
sudo certbot certonly --standalone \
-d infisical.example.com \
--email admin@example.com \
--agree-tos \
--non-interactive
# Copy certificates
sudo cp /etc/letsencrypt/live/infisical.example.com/fullchain.pem nginx/certs/cert.pem
sudo cp /etc/letsencrypt/live/infisical.example.com/privkey.pem nginx/certs/key.pem
sudo chmod 644 nginx/certs/*.pem
cat > nginx/nginx.conf << 'EOF'
server {
listen 443 ssl http2;
server_name infisical.example.com;
# SSL Configuration
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# Logging
access_log /var/log/nginx/infisical_access.log;
error_log /var/log/nginx/infisical_error.log;
location / {
proxy_pass http://infisical-backend:8080;
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_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Health check endpoint (no auth required for monitoring)
location /api/status {
proxy_pass http://infisical-backend:8080;
access_log off;
}
}
# HTTP to HTTPS redirect
server {
listen 80;
server_name infisical.example.com;
return 301 https://$server_name$request_uri;
}
EOF
cat > docker-compose.nginx.yml << 'EOF'
services:
nginx:
image: nginx:alpine
container_name: infisical-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./nginx/certs:/etc/nginx/certs:ro
networks:
- infisical
depends_on:
- backend
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 30s
timeout: 10s
retries: 3
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
security_opt:
- no-new-privileges:true
EOF
# Start with Nginx reverse proxy
docker compose -f docker-compose.yml -f docker-compose.nginx.yml up -d
# Or start without Nginx (backend only)
docker compose up -d
# Check all containers are running
docker compose ps
# View backend logs
docker compose logs -f backend
# Test health endpoint
curl -k https://localhost/api/status
# Check network connectivity
docker compose exec backend ping -c 3 db
docker compose exec backend ping -c 3 redis
https://infisical.example.comThe compose file above includes these security measures:
| Setting | Purpose |
|---|---|
read_only: true |
Prevents container filesystem writes |
cap_drop: ALL |
Removes all Linux capabilities |
security_opt: no-new-privileges |
Prevents privilege escalation |
tmpfs |
Provides writable temp space without persistence |
| Resource limits | Prevents resource exhaustion attacks |
# Configure firewall (UFW example)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP (for Let's Encrypt)
sudo ufw allow 443/tcp # HTTPS
sudo ufw --force enable
For production, consider using an external managed database:
# In .env file
DB_CONNECTION_URI=postgres://user:password@managed-db.example.com:5432/infisical?sslmode=require
DB_ROOT_CERT=/path/to/ca-cert.pem
Mount the certificate in the compose file:
services:
backend:
volumes:
- /path/to/ca-cert.pem:/etc/ssl/certs/db-ca.pem:ro
environment:
- DB_ROOT_CERT=/etc/ssl/certs/db-ca.pem
cat > backup.sh << 'EOF'
#!/bin/bash
set -e
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)
DB_CONTAINER="infisical-db"
DB_USER="infisical"
DB_NAME="infisical"
RETENTION_DAYS=7
echo "Starting Infisical backup: $DATE"
# Create backup directory
mkdir -p $BACKUP_DIR/postgres
# Backup PostgreSQL database
echo "Backing up database..."
docker exec $DB_CONTAINER pg_dump -U $DB_USER $DB_NAME | \
gzip > $BACKUP_DIR/postgres/infisical_db_$DATE.sql.gz
# Backup environment file (contains encryption keys)
echo "Backing up configuration..."
cp .env $BACKUP_DIR/infisical_env_$DATE.backup
# Backup Redis data (optional, for cache persistence)
# docker exec infisical-redis redis-cli BGSAVE
# Cleanup old backups
echo "Cleaning up backups older than $RETENTION_DAYS days..."
find $BACKUP_DIR/postgres -name "*.gz" -mtime +$RETENTION_DAYS -delete
find $BACKUP_DIR -name "*.backup" -mtime +$RETENTION_DAYS -delete
echo "Backup completed successfully: $DATE"
EOF
chmod +x backup.sh
# Add to crontab (daily at 2 AM)
(crontab -l 2>/dev/null; echo "0 2 * * * /path/to/backup.sh") | crontab -
# Stop Infisical
docker compose down
# Restore database
gunzip -c backups/postgres/infisical_db_YYYYMMDD_HHMMSS.sql.gz | \
docker exec -i infisical-db psql -U infisical -d infisical
# Restore environment file
cp backups/infisical_env_YYYYMMDD_HHMMSS.backup .env
# Start Infisical
docker compose up -d
# Pull latest image
docker compose pull
# Update version tag in docker-compose.yml if using specific version
# Then restart
docker compose down
docker compose up -d
# Verify migration completed
docker compose logs db-migration
# All services
docker compose logs -f
# Specific service
docker compose logs -f backend
# Last 100 lines
docker compose logs --tail=100 backend
# Container resource usage
docker stats infisical-backend infisical-db infisical-redis
# Disk usage
docker system df -v
# Database size
docker exec infisical-db psql -U infisical -d infisical -c \
"SELECT pg_size_pretty(pg_database_size('infisical'));"
# Check logs
docker compose logs backend
# Verify environment
docker compose config
# Test database connectivity
docker compose exec backend nc -zv db 5432
# Re-run migrations
docker compose run --rm db-migration
# Check database schema
docker compose exec db psql -U infisical -d infisical -c "\dt"
# Check container memory
docker stats --no-stream
# Adjust resource limits in docker-compose.yml
# Then restart
docker compose down
docker compose up -d
# Verify certificate
openssl x509 -in nginx/certs/cert.pem -text -noout
# Test SSL connection
openssl s_client -connect infisical.example.com:443
| Variable | Required | Description | Example |
|---|---|---|---|
SITE_URL |
Yes | Public URL of Infisical instance | https://infisical.example.com |
ENCRYPTION_KEY |
Yes | 32-char hex key for encryption | openssl rand -hex 16 |
AUTH_SECRET |
Yes | Base64 secret for JWT | openssl rand -base64 32 |
DB_CONNECTION_URI |
Yes | PostgreSQL connection string | postgres://user:pass@host:5432/db |
REDIS_URL |
Yes | Redis connection URL | redis://redis:6379 |
SMTP_HOST |
No | SMTP server for emails | smtp.example.com |
SMTP_PORT |
No | SMTP port | 587 |
TELEMETRY_ENABLED |
No | Enable telemetry | false |
Any questions?
Feel free to contact us. Find all contact information on our contact page.