Skip to content

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

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):

  1. Primary: Hetzner server ~/infisical/.env (original)
  2. Secondary: Kimsufi server (cross-server, GPG encrypted)
  3. Tertiary: Cloud storage (Google Drive/Dropbox, GPG encrypted)
  4. 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 accounts
  • dev_edu1 - Educational accounts
  • dev_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:

infisical login --domain=https://secrets.kua.cl

Or with Tailscale IP:

infisical login --domain=http://100.80.53.55:8080

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:

infisical login --domain=https://secrets.kua.cl


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):

# If .infisical.json exists in current directory
infisical secrets --env=dev

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

infisical secrets delete TEMP_API_KEY --env=dev --projectId=personal-vault

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:

❌ Error: Not logged in to Infisical
Please run: infisical login --domain=https://secrets.kua.cl

If project doesn't exist:

❌ Error: Project 'personal-vault' not found
Available projects:
- project-1
- terraform

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):

# Weekly MongoDB backup
0 2 * * 0 /usr/local/bin/backup-infisical-mongodb.sh


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

  1. Log in to https://secrets.kua.cl
  2. Select project (e.g., "personal-vault")
  3. Select environment (e.g., "dev")
  4. Click "Add Secret"
  5. Enter key: ANTHROPIC_API_KEY
  6. Enter value: sk-ant-...
  7. Optional: Add comment
  8. Click "Save"

Bulk import: 1. Select project & environment 2. Click "Import" (top right) 3. Paste dotenv format:

ANTHROPIC_API_KEY=sk-ant-...
GOOGLE_API_KEY=AIza...
DATABASE_URL=postgresql://...
4. Click "Import"


Troubleshooting

"Not logged in to Infisical"

Cause: CLI not authenticated

Fix:

infisical login --domain=https://secrets.kua.cl

"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


Security Best Practices

  1. Backup ENCRYPTION_KEY to multiple locations (cloud + USB)
  2. Encrypt backups with GPG before cloud storage
  3. Store passphrase in password manager (1Password, Bitwarden)
  4. Cross-server backup (Hetzner → Kimsufi)
  5. Weekly MongoDB backups (automated cron)
  6. Test recovery periodically (ensure backups work)
  7. NEVER commit .env to git (even private repos)
  8. NEVER email encryption keys
  9. NEVER put SSH keys in Infisical
  10. NEVER share ENCRYPTION_KEY in Slack/Discord