Infisical Secrets Management¶
Self-hosted secrets manager at secrets.kua.cl - Complete architecture and usage guide.
⚠️ MOST CRITICAL DOCUMENT: Contains backup procedures for the ENCRYPTION_KEY.
Table of Contents¶
- What is Infisical
- Architecture
- The ENCRYPTION_KEY - CRITICAL
- Project Structure
- CLI Installation & Authentication
- CLI Usage
- Tools vs Credentials
- Integration with dev() Function
- Backup Strategy
- Disaster Recovery
- Web UI Access
- Troubleshooting
What is Infisical¶
Overview¶
Infisical is a self-hosted secrets management platform that replaces Doppler.
Why Infisical: - ✅ Self-hosted: Full control, no vendor lock-in - ✅ Unlimited projects: No restrictions - ✅ Free forever: Open source, no usage limits - ✅ Similar to Doppler: Easy migration, familiar workflow - ✅ Secure: End-to-end encryption with ENCRYPTION_KEY
Deployed at: - HTTPS: https://secrets.kua.cl (via Traefik + Let's Encrypt) - HTTP: http://100.80.53.55:8080 (Tailscale only) - Server: Hetzner VPS (Germany)
What It Manages¶
✅ Application secrets: - API keys (OpenAI, Anthropic, Google, AWS, etc.) - Database credentials (PostgreSQL, MongoDB, Redis passwords) - Service tokens (GitHub, Stripe, Twilio, Cloudflare, etc.) - Environment-specific configurations
❌ What it does NOT manage:
- SSH private keys (see SSH Keys Guide)
- Infisical's own ENCRYPTION_KEY (stored in ~/infisical/.env)
- Docker images or application code
Architecture¶
Component Diagram¶
┌─────────────────────────────────────────────────────────┐
│ INFISICAL SELF-HOSTED ARCHITECTURE │
│ (secrets.kua.cl) │
└─────────────────────────────────────────────────────────┘
CLIENT ACCESS
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Web UI │ │ Infisical CLI│ │ API Clients │
│ Browser │ │ (on servers) │ │ (apps) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└─────────────────┼─────────────────┘
│ HTTPS/HTTP
▼
┌─────────────────────────────────┐
│ Traefik Reverse Proxy │
│ - SSL termination │
│ - secrets.kua.cl routing │
│ - Let's Encrypt certs │
└─────────────┬───────────────────┘
│
▼
┌─────────────────────────────────┐
│ Infisical Backend (Docker) │
│ - Node.js application │
│ - Authentication │
│ - Encryption/Decryption │
│ - Uses ENCRYPTION_KEY │
└────┬────────────────┬───────────┘
│ │
┌───────┴────┐ ┌──────┴──────┐
│ │ │ │
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────────┐
│MongoDB │ │ Redis │ │ PostgreSQL │
│(Primary)│ │ (Cache) │ │ (Shared DB) │
│ │ │ │ │ │
│Secrets │ │Sessions │ │User/Project │
│Encrypted│ │Ephemeral│ │Metadata │
│with │ │ │ │ │
│ENCRYPTION│ │ │ │ │
│_KEY │ │ │ │ │
└─────────┘ └─────────┘ └─────────────┘
Services¶
| Service | Purpose | Persistence | Critical? |
|---|---|---|---|
| Infisical Backend | API, encryption, auth | Stateless | ⚠️ Yes |
| MongoDB | Encrypted secrets storage | ✅ Persistent | 🔴 CRITICAL |
| Redis | Session cache, rate limiting | ❌ Ephemeral | ⚠️ Can rebuild |
| PostgreSQL | User accounts, project metadata | ✅ Persistent | ⚠️ Can rebuild |
| Traefik | Reverse proxy, SSL | Stateless | ⚠️ Yes |
Data Flow¶
1. User/CLI requests secret
↓
2. Traefik routes to Infisical
↓
3. Infisical authenticates (PostgreSQL)
↓
4. Infisical fetches from MongoDB (encrypted blob)
↓
5. Infisical decrypts using ENCRYPTION_KEY
↓
6. Returns plaintext secret to client
The ENCRYPTION_KEY - CRITICAL¶
What Is It?¶
The ENCRYPTION_KEY is the master encryption key stored in ~/infisical/.env on the Hetzner server.
Purpose: Encrypts and decrypts ALL secrets stored in MongoDB.
┌─────────────────────────────────────────────┐
│ ~/infisical/.env │
│ ┌──────────────────────────────────────┐ │
│ │ MONGO_PASSWORD=abc123... │ │
│ │ ENCRYPTION_KEY=def456... ⭐ CRITICAL! │ │
│ │ AUTH_SECRET=ghi789... │ │
│ │ SITE_URL=https://secrets.kua.cl │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
│
│ Used to encrypt/decrypt
▼
┌─────────────────────────────────────────────┐
│ MongoDB Database │
│ ┌──────────────────────────────────────┐ │
│ │ Encrypted Secrets (binary blobs): │ │
│ │ - OPENAI_API_KEY=❌❌❌❌... │ │
│ │ - ANTHROPIC_API_KEY=❌❌❌... │ │
│ │ - DATABASE_URL=❌❌❌❌❌... │ │
│ └──────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
What's in the .env File?¶
| Variable | Purpose | Sensitive? | Can Regenerate? |
|---|---|---|---|
MONGO_PASSWORD |
MongoDB login password | ⚠️ Medium | ✅ Yes (with effort) |
ENCRYPTION_KEY |
Master encryption key | 🔴 CRITICAL | ❌ NO - If lost, all secrets lost forever! |
AUTH_SECRET |
JWT tokens for user sessions | ⚠️ Medium | ✅ Yes (users re-login) |
SITE_URL |
URL where Infisical is hosted | ✅ Safe | ✅ Yes (just a URL) |
The Critical Part¶
If ENCRYPTION_KEY is lost: - ❌ Cannot decrypt any secrets from MongoDB - ❌ ALL your API keys are permanently lost - ❌ Must re-enter ALL secrets manually - ❌ MongoDB backups are useless (encrypted with lost key) - 💀 Complete data loss scenario
With ENCRYPTION_KEY backup: - ✅ Can decrypt all secrets - ✅ Can restore from MongoDB backup - ✅ Can migrate to new server - ✅ Full recovery possible
MUST BACKUP¶
Backup locations (recommended: all 4):
- Primary: Hetzner server
~/infisical/.env(original) - Secondary: Kimsufi server (cross-server, GPG encrypted)
- Tertiary: Cloud storage (Google Drive/Dropbox, GPG encrypted)
- Quaternary: Encrypted USB in physical safe
See Backup Strategy below for detailed instructions.
Project Structure¶
Hierarchy¶
Infisical
├── Projects (like vaults)
│ ├── personal-vault
│ ├── project-1
│ └── terraform
│
└── Environments (per project)
├── dev
├── staging
└── prod
Terminology Mapping¶
| Doppler | Infisical |
|---|---|
| Project | Project |
| Config | Environment |
| Secret | Secret |
Example: personal-vault project
personal-vault (Project)
├── dev (Environment)
│ ├── ANTHROPIC_API_KEY=sk-ant-...
│ ├── GOOGLE_API_KEY=AIza...
│ ├── DATABASE_URL=postgresql://...
│ └── AWS_ACCESS_KEY=AKIA...
│
├── staging (Environment)
│ ├── ANTHROPIC_API_KEY=sk-ant-staging-...
│ └── DATABASE_URL=postgresql://staging...
│
└── prod (Environment)
├── ANTHROPIC_API_KEY=sk-ant-prod-...
└── DATABASE_URL=postgresql://prod...
Custom Environments¶
You can create custom environments beyond dev/staging/prod:
dev_personal- Personal AI accountsdev_edu1- Educational accountsdev_work- Work accounts
How to create: 1. Web UI: Project → Settings → Environments → Create Environment 2. CLI: Currently must use web UI
CLI Installation & Authentication¶
Installation (Servers)¶
On Hetzner:
# Already installed if you used setup script
infisical --version
# If not installed:
curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | sudo -E bash
sudo apt-get update
sudo apt-get install infisical
On Kimsufi:
# Same as above
curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | sudo -E bash
sudo apt-get update
sudo apt-get install infisical
Authentication¶
First time:
Or with Tailscale IP:
Prompts:
? How would you like to login? Universal Auth
? Email: kavi@example.com
? Password: [hidden]
✓ Logged in successfully
Where credentials are stored:
- ~/.config/infisical/ (CLI config)
- Token is cached, no need to re-authenticate each time
Check authentication:
infisical whoami
# Output:
# Logged in as: kavi@example.com
# Infisical instance: https://secrets.kua.cl
Re-authenticate:
CLI Usage¶
List Projects¶
infisical projects
# Output:
# ID NAME CREATED
# abc123... personal-vault 2024-12-20
# def456... project-1 2024-12-21
# ghi789... terraform 2024-12-22
List Secrets¶
# List secrets in personal-vault / dev
infisical secrets --env=dev --projectId=personal-vault
# Output:
# KEY VALUE COMMENT
# ANTHROPIC_API_KEY sk-ant-... Claude API key
# GOOGLE_API_KEY AIza... Gemini API key
# DATABASE_URL postgresql://... Dev database
Shortcut (if you're already in a project directory):
Get Single Secret¶
infisical secrets get ANTHROPIC_API_KEY --env=dev --projectId=personal-vault
# Output:
# sk-ant-api03-abc123...
Set Secret¶
# Set or update a secret
infisical secrets set ANTHROPIC_API_KEY="sk-ant-new-value" --env=dev --projectId=personal-vault
# Set with comment
infisical secrets set DATABASE_URL="postgresql://localhost:5432/mydb" \
--env=dev \
--projectId=personal-vault \
--comment="Local development database"
Delete Secret¶
Export All Secrets (for dev() function)¶
# Export to dotenv format
infisical export --env=dev --projectId=personal-vault --format=dotenv
# Output (printed to stdout):
# ANTHROPIC_API_KEY=sk-ant-...
# GOOGLE_API_KEY=AIza...
# DATABASE_URL=postgresql://...
# Save to file (used by dev() function)
infisical export --env=dev --projectId=personal-vault --format=dotenv > /tmp/secrets.env
Run Command with Secrets Injected¶
# Run any command with secrets as environment variables
infisical run --env=dev --projectId=personal-vault -- node app.js
infisical run --env=dev --projectId=personal-vault -- python main.py
# Equivalent to:
# 1. Export secrets to temp file
# 2. Source temp file
# 3. Run command
# 4. Delete temp file
Tools vs Credentials¶
What Goes in Docker Image¶
✅ Tools (safe to include):
# In my-dev-env:latest Docker image
RUN npm install -g infisical # CLI tool
RUN apt-get install -y infisical # CLI tool
Why safe: - Just the executable program - No authentication data - Same for everyone - Can be shared publicly
What NEVER Goes in Docker Image¶
❌ Credentials (NEVER include):
# ❌ NEVER DO THIS
COPY ~/.config/infisical/ /root/.config/infisical/
ENV INFISICAL_TOKEN=eyJhbGc...
ENV ENCRYPTION_KEY=def456...
Why dangerous: - Contains authentication tokens - Anyone with image = access to your secrets - Can't be removed from Docker layers - If pushed to Docker Hub = public exposure
The Correct Flow¶
HOST (Hetzner/Kimsufi)
│
├─ ~/.config/infisical/ (authentication token)
├─ ~/infisical/.env (ENCRYPTION_KEY)
│
▼
dev() FUNCTION
│
├─ Runs: infisical export --env=dev --projectId=personal-vault
├─ Saves to: /tmp/env-$$.list
│
▼
DOCKER CONTAINER
│
├─ Image has: infisical CLI (tool only)
├─ Runtime gets: Secrets via --env-file /tmp/env-$$.list
├─ Never has: Infisical token, ENCRYPTION_KEY
│
▼
CLEANUP
│
└─ rm /tmp/env-$$.list (automatic on exit)
Key principle: Tools in image, credentials on host, secrets injected at runtime.
Integration with dev() Function¶
How dev() Uses Infisical¶
Step-by-step:
# 1. User runs dev
dev my-project
# 2. Prompts for Infisical project & environment
🔐 Select Infisical project: personal-vault
🌍 Select environment: dev
# 3. Authenticates (checks whoami)
infisical whoami
# If not authenticated: prompts to run infisical login
# 4. Exports secrets to temporary file
infisical export --env=dev --projectId=personal-vault --format=dotenv > /tmp/env-$$.list
# File contains:
# ANTHROPIC_API_KEY=sk-ant-...
# GOOGLE_API_KEY=AIza...
# DATABASE_URL=postgresql://...
# 5. Launches Docker with secrets
docker run --rm -it \
--env-file /tmp/env-$$.list \
-v ~/Coding:/app \
my-dev-env:latest
# 6. Cleanup on exit (trap)
rm -f /tmp/env-$$.list
Inside Container¶
# Secrets available as environment variables
echo $ANTHROPIC_API_KEY
# Output: sk-ant-api03-...
# AI agents use them automatically
claude # Uses ANTHROPIC_API_KEY from environment
gemini # Uses GOOGLE_API_KEY from environment
# Application code reads them
python -c "import os; print(os.getenv('DATABASE_URL'))"
Error Handling¶
If infisical not authenticated:
If project doesn't exist:
If environment doesn't exist:
❌ Error: Environment 'dev' not found in project 'personal-vault'
Available environments: staging, prod
Backup Strategy¶
What to Backup¶
| Item | Location | Critical? | Frequency |
|---|---|---|---|
| ENCRYPTION_KEY (in .env) | ~/infisical/.env |
🔴 CRITICAL | Once, then on change |
| MongoDB data | Docker volume | ⚠️ Important | Weekly |
| PostgreSQL data | Docker volume | ⚠️ Can rebuild | Monthly |
| Redis data | Docker volume | ✅ Ephemeral | Never (just cache) |
Backup ENCRYPTION_KEY¶
Method 1: GPG Encrypted to Cloud (Recommended)
# On Hetzner
cd ~/infisical
# Encrypt .env file
gpg --symmetric --cipher-algo AES256 .env
# Passphrase: [STRONG PASSPHRASE - save in password manager]
# This creates: .env.gpg (encrypted)
# Copy to Kimsufi (cross-server backup)
scp .env.gpg kimsufi:~/infisical-hetzner-backup.env.gpg
# Download to MacBook
scp hetzner:~/infisical/.env.gpg ~/Desktop/infisical-backup.env.gpg
# Upload to cloud (Google Drive, Dropbox, iCloud)
# File is encrypted, safe to store in cloud
Method 2: Encrypted USB (Ultra-Paranoid)
# Download from server
scp hetzner:~/infisical/.env ~/Desktop/infisical-backup.env
# Copy to encrypted USB drive
cp ~/Desktop/infisical-backup.env /Volumes/MyEncryptedUSB/
# Delete local copy
rm ~/Desktop/infisical-backup.env
# Store USB in physical safe
Method 3: Both (Best Practice)
Combine both approaches: - Primary: GPG encrypted in cloud (accessible anywhere) - Secondary: Plain on encrypted USB in safe (ultimate fallback)
Total recommended copies: 4-5
- Original: Hetzner ~/infisical/.env
- Backup 1: Kimsufi (GPG encrypted)
- Backup 2: Cloud storage (GPG encrypted)
- Backup 3: Encrypted USB in safe
- Backup 4: Password manager (if key is small enough)
Backup MongoDB Data¶
# On Hetzner
cd ~/infisical
# Create MongoDB backup
docker exec infisical-mongo mongodump --out=/tmp/backup
# Copy from container
docker cp infisical-mongo:/tmp/backup ./mongodb-backup-$(date +%Y%m%d)
# Compress
tar -czf mongodb-backup-$(date +%Y%m%d).tar.gz mongodb-backup-$(date +%Y%m%d)
rm -rf mongodb-backup-$(date +%Y%m%d)
# Encrypt backup (optional but recommended)
gpg --symmetric --cipher-algo AES256 mongodb-backup-$(date +%Y%m%d).tar.gz
# Copy to Kimsufi
scp mongodb-backup-$(date +%Y%m%d).tar.gz.gpg kimsufi:~/backups/
Automate weekly (add to crontab):
Disaster Recovery¶
Scenario 1: Lost .env but Have Backup¶
Symptoms: Infisical won't start, "Invalid ENCRYPTION_KEY" error
Recovery:
# Download from cloud or USB
gpg --decrypt infisical-backup.env.gpg > .env
# Or copy from Kimsufi
scp kimsufi:~/infisical-hetzner-backup.env.gpg .
gpg --decrypt infisical-hetzner-backup.env.gpg > .env
# Copy to server
scp .env hetzner:~/infisical/
# Restart Infisical
ssh hetzner
cd ~/infisical
docker-compose restart
# Verify
infisical whoami
Result: ✅ Full recovery, all secrets accessible
Time to recovery: 5-10 minutes
Scenario 2: Lost .env AND No Backup¶
Symptoms: Same as above, but no backup available
Recovery:
# ❌ CANNOT RECOVER SECRETS
# All encrypted data in MongoDB is permanently lost
# Must:
# 1. Re-deploy Infisical with new ENCRYPTION_KEY
# 2. Manually re-enter ALL API keys and secrets
# 3. Update all applications with new secrets
Result: 💀 All secrets lost forever
Time to recovery: Hours to days (depending on number of secrets)
Prevention: BACKUP THE .env FILE (see above)
Scenario 3: MongoDB Data Lost¶
Symptoms: Infisical starts, but all secrets are empty
Recovery:
# If you have MongoDB backup:
scp kimsufi:~/backups/mongodb-backup-YYYYMMDD.tar.gz.gpg .
gpg --decrypt mongodb-backup-YYYYMMDD.tar.gz.gpg | tar -xz
# Restore to MongoDB
docker cp mongodb-backup-YYYYMMDD infisical-mongo:/tmp/backup
docker exec infisical-mongo mongorestore /tmp/backup
# Restart Infisical
docker-compose restart
If no MongoDB backup:
# ENCRYPTION_KEY still works
# Just re-enter secrets in Infisical web UI
# They'll be encrypted with existing key
Result: ⚠️ Lost old secrets, but can create new ones
Scenario 4: Complete Server Loss (Hetzner Down)¶
Symptoms: Hetzner VPS unreachable
Recovery:
# 1. Provision new Hetzner VPS (via Hetzner Cloud Console)
# 2. Deploy Infisical
scp ~/Coding/.docker-image/infisical new-hetzner:~/
scp ~/Desktop/infisical-backup.env.gpg new-hetzner:~/
ssh new-hetzner
gpg --decrypt infisical-backup.env.gpg > ~/infisical/.env
cd ~/infisical
docker-compose up -d
# 3. Restore MongoDB backup (if available)
scp kimsufi:~/backups/mongodb-backup-latest.tar.gz.gpg .
gpg --decrypt mongodb-backup-latest.tar.gz.gpg | tar -xz
docker cp mongodb-backup new-infisical-mongo:/tmp/backup
docker exec new-infisical-mongo mongorestore /tmp/backup
# 4. Update DNS (if IP changed)
# Update A record for secrets.kua.cl to new IP
# 5. Update Infisical CLI authentication on all servers
ssh kimsufi
infisical login --domain=https://secrets.kua.cl
Result: ✅ Full recovery with backups
Time to recovery: 30-60 minutes
Web UI Access¶
URLs¶
- HTTPS (public): https://secrets.kua.cl
- HTTP (Tailscale): http://100.80.53.55:8080
Features¶
Dashboard: - View all projects - View secrets per environment - Add/edit/delete secrets - Manage team members - Audit logs
Projects: - Create new projects - Configure environments (dev, staging, prod, custom) - Set project-level permissions
Settings: - User profile - API tokens - Webhook integrations - Audit logs
Creating Secrets via Web UI¶
- Log in to https://secrets.kua.cl
- Select project (e.g., "personal-vault")
- Select environment (e.g., "dev")
- Click "Add Secret"
- Enter key:
ANTHROPIC_API_KEY - Enter value:
sk-ant-... - Optional: Add comment
- Click "Save"
Bulk import: 1. Select project & environment 2. Click "Import" (top right) 3. Paste dotenv format:
4. Click "Import"Troubleshooting¶
"Not logged in to Infisical"¶
Cause: CLI not authenticated
Fix:
"Project not found"¶
Cause: Project ID or name incorrect
Fix:
# List all projects
infisical projects
# Use exact project ID or name
infisical secrets --env=dev --projectId=<EXACT_ID>
"Environment not found"¶
Cause: Environment doesn't exist in project
Fix: 1. Go to Web UI: https://secrets.kua.cl 2. Project → Settings → Environments 3. Create environment (e.g., "dev_personal") 4. Try again
"Connection refused"¶
Cause: Infisical service is down
Fix:
ssh hetzner
cd ~/infisical
docker-compose ps # Check status
# If stopped, restart
docker-compose up -d
# Check logs
docker-compose logs infisical
"Invalid ENCRYPTION_KEY"¶
Cause: .env file is corrupted or missing
Fix: Restore from backup (see Disaster Recovery)
Secrets not loading in container¶
Cause: dev() function export failed
Fix:
# Test export manually
infisical export --env=dev --projectId=personal-vault --format=dotenv
# Should output secrets
# If empty, check:
# 1. Are you authenticated? infisical whoami
# 2. Does project exist? infisical projects
# 3. Do secrets exist? infisical secrets --env=dev
Quick Reference¶
Essential Commands¶
# Authentication
infisical login --domain=https://secrets.kua.cl
infisical whoami
# List
infisical projects
infisical secrets --env=dev --projectId=personal-vault
# Get
infisical secrets get KEY_NAME --env=dev --projectId=personal-vault
# Set
infisical secrets set KEY="value" --env=dev --projectId=personal-vault
# Export (for dev() function)
infisical export --env=dev --projectId=personal-vault --format=dotenv
# Run with secrets
infisical run --env=dev --projectId=personal-vault -- command
Backup Commands¶
# Backup ENCRYPTION_KEY
cd ~/infisical
gpg --symmetric --cipher-algo AES256 .env
scp .env.gpg kimsufi:~/infisical-hetzner-backup.env.gpg
# Backup MongoDB
docker exec infisical-mongo mongodump --out=/tmp/backup
docker cp infisical-mongo:/tmp/backup ./mongodb-backup
tar -czf mongodb-backup.tar.gz mongodb-backup
# Restore .env
gpg --decrypt infisical-backup.env.gpg > .env
# Restore MongoDB
docker cp mongodb-backup infisical-mongo:/tmp/backup
docker exec infisical-mongo mongorestore /tmp/backup
Related Documentation¶
- SSH Keys Guide - What NOT to put in Infisical
- dev() Function Guide - How dev() uses Infisical
- Docker Environment - Tools vs credentials
- Emergency Access - Disaster recovery procedures
- First-Time Setup - Initial Infisical setup
Security Best Practices¶
- ✅ Backup ENCRYPTION_KEY to multiple locations (cloud + USB)
- ✅ Encrypt backups with GPG before cloud storage
- ✅ Store passphrase in password manager (1Password, Bitwarden)
- ✅ Cross-server backup (Hetzner → Kimsufi)
- ✅ Weekly MongoDB backups (automated cron)
- ✅ Test recovery periodically (ensure backups work)
- ❌ NEVER commit .env to git (even private repos)
- ❌ NEVER email encryption keys
- ❌ NEVER put SSH keys in Infisical
- ❌ NEVER share ENCRYPTION_KEY in Slack/Discord