11 KiB
Infisical (Secrets Management)
Self-hosted Infisical instance running as a Docker stack on DOCKER (10.100.0.35). Deployed via Ansible (role infisical in the ansiblearr playbook); deployed 2026-04-29.
Access
- URL: https://infisical.dohertylan.com (via Traefik + Cloudflare cert)
- Auth: native Infisical accounts (no Authelia middleware — Infisical has its own login)
- First-time setup: first sign-up becomes the admin. After bootstrapping, set
INVITE_ONLY_SIGNUP=trueinroles/infisical/defaults/main.ymland re-deploy to lock further signups.
Stack layout (on docker host)
/opt/infisical/ (compose project name infisical; service labels carry standard com.docker.compose.* only — no custom project=lmxopcua-style label):
| Container | Image | Internal port | Host port | Volume |
|---|---|---|---|---|
infisical |
infisical/infisical:latest-postgres |
8080 (HTTP) | none — Traefik handles 443→8080 | — |
infisical-db |
postgres:16-alpine |
5432 | none | /opt/infisical/postgres_data |
infisical-redis |
redis:7-alpine |
6379 | none | /opt/infisical/redis_data |
Networks: traefik (external, shared with the rest of the stack) and infisical (internal-only bridge for db + redis).
Configuration
Source of truth: roles/infisical/defaults/main.yml in the ansible repo. Re-deploys overwrite /opt/infisical/docker-compose.yml, so don't edit it on the host.
| Variable | Value (default) | Notes |
|---|---|---|
infisical_image |
infisical/infisical:latest-postgres |
Standalone all-in-one image (API + frontend) |
infisical_subdomain |
infisical |
Becomes infisical.dohertylan.com |
infisical_db_user / _db_name |
infisical / infisical |
Internal-only — not exposed past the compose network |
infisical_db_password |
[Infisical: homelab/apps/infisical/DB_PWD] |
Plaintext also lives in roles/infisical/defaults/main.yml (chicken-and-egg: the playbook must hold the value to deploy Infisical from scratch) |
infisical_encryption_key |
32 hex chars | Do not change after data exists — used to envelope-encrypt secrets at rest. Changing it makes existing secrets unrecoverable; use Infisical's key-rotation flow if you need to rotate |
infisical_auth_secret |
random base64 | JWT signing key |
infisical_telemetry_enabled |
false |
Anonymous telemetry opted out |
Deploy / update
Three equivalent paths:
# Via Semaphore UI (or API): http://10.100.0.35:3000 → template "Deploy Full Stack"
curl -s -c - http://localhost:3000/api/auth/login -X POST -H 'Content-Type: application/json' \
-d '{"auth":"dohertj2","password":"<semaphore-admin-pwd>"}' | grep semaphore | awk '{print $NF}' \
| xargs -I{} curl -s -b "semaphore={}" -X POST http://localhost:3000/api/project/1/tasks \
-H 'Content-Type: application/json' -d '{"template_id":8,"project_id":1}'
# Or directly on the docker host (skips going through Semaphore/git):
ssh dohertj2@10.100.0.35 'cd /opt/infisical && docker compose up -d'
# Or pull image only (without restart):
ssh dohertj2@10.100.0.35 'cd /opt/infisical && docker compose pull'
To make a change, edit roles/infisical/defaults/main.yml (or the template), commit + push to GitHub, then re-run the Semaphore template.
Operations
# Status
ssh dohertj2@10.100.0.35 'docker ps --filter name=infisical'
# Logs
ssh dohertj2@10.100.0.35 'cd /opt/infisical && docker compose logs --tail=200 infisical'
# Restart just the app (keep db/redis up)
ssh dohertj2@10.100.0.35 'cd /opt/infisical && docker compose restart infisical'
# Full stack restart
ssh dohertj2@10.100.0.35 'cd /opt/infisical && docker compose up -d --force-recreate'
# psql shell into the db
ssh dohertj2@10.100.0.35 'docker exec -it infisical-db psql -U infisical -d infisical'
Backups
Only state worth preserving: /opt/infisical/postgres_data. Redis is just a cache.
# Hot logical dump (run on docker host)
docker exec infisical-db pg_dump -U infisical -d infisical | gzip > /mnt/share/backups/infisical-$(date +%F).sql.gz
Restore: stop the stack, drop and recreate the DB, gunzip < dump.sql.gz | docker exec -i infisical-db psql -U infisical -d infisical, start the stack. Whatever encryption key was in defaults/main.yml at backup time must still be in place — the restored ciphertext is only readable with the same ENCRYPTION_KEY.
Client setup (CLI on other machines)
Goal: from any host that needs a credential, run infisical secrets get … (or infisical run -- <cmd>) instead of finding the value in a .md file.
1. Create a per-host Machine Identity
One per host — never share. Each is independently revocable and shows up cleanly in the audit log.
- https://infisical.dohertylan.com → Org Settings → Access Control → Identities → Add
- Name it
claude-<hostname>(e.g.,claude-mac,claude-ww-vm,claude-docker). Auth method: Universal Auth. - Open the new identity → Client Secrets → Create Client Secret → save the
client_id+client_secret(only shown once). - (Optional but recommended) Open the identity → Authentication → Universal Auth → set Client Secret Trusted IPs to
10.100.0.0/24so the credential is unusable off-LAN. - Open the Homelab project → Access Control → Identities → Add → pick the new identity, role Viewer (read-only). Promote to Developer/Admin only if the host needs to write secrets.
2. Install the CLI
Linux (Debian/Ubuntu/Trixie):
curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | sudo -E bash
sudo apt-get install -y infisical
macOS:
brew install infisical/get-cli/infisical
Windows (PowerShell, admin):
winget install --id Infisical.InfisicalCLI -e
# or:
scoop bucket add main; scoop install infisical
Verify with infisical --version.
3. Configure auth + endpoint
The CLI reads INFISICAL_API_URL for the host (defaults to Infisical Cloud — must be overridden for self-hosted) and either env vars or a saved login for credentials. Recommended: env vars in your shell rc / Windows user env.
Linux/macOS (~/.bashrc, ~/.zshrc):
export INFISICAL_API_URL=https://infisical.dohertylan.com
export INFISICAL_UNIVERSAL_AUTH_CLIENT_ID="<client-id>"
export INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET="<client-secret>"
Windows (PowerShell — sets persistent user env):
[Environment]::SetEnvironmentVariable('INFISICAL_API_URL','https://infisical.dohertylan.com','User')
[Environment]::SetEnvironmentVariable('INFISICAL_UNIVERSAL_AUTH_CLIENT_ID','<client-id>','User')
[Environment]::SetEnvironmentVariable('INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET','<client-secret>','User')
# Open a new shell for these to take effect.
Each infisical command will use Universal Auth automatically when these are set. No infisical login needed.
4. Verify
# Should print one or more secrets from the homelab project
infisical secrets --env=infrastructure --path=/esxi --silent
# Get a single value (the GOVC password):
infisical secrets get GOVC_PASSWORD --env=infrastructure --path=/esxi --plain
# Run a command with secrets injected as env vars:
infisical run --env=infrastructure --path=/esxi -- printenv GOVC_PASSWORD
If the project ID can't be resolved automatically, pin it:
infisical secrets get GOVC_PASSWORD \
--projectId=e36459c8-b071-4b86-a43c-795b31e75584 \
--env=infrastructure --path=/esxi --plain
5. Common usage patterns
# govc — fetch the password into env via `infisical run`, no plaintext on disk
infisical run --env=infrastructure --path=/esxi -- \
bash -c 'GOVC_URL=https://10.2.0.12/sdk GOVC_USERNAME=govc GOVC_INSECURE=true \
govc vm.info WW_DEV_VM'
# .NET app reads SQL_DEV_SA_PWD from env at startup
infisical run --env=dev --path=/lmxopcua -- \
dotnet run --project src/ZB.MOM.WW.OtOpcUa.Server
# Docker compose with secret env injection
infisical run --env=apps --path=/gitea -- docker compose up -d
# Shell into one secret quickly:
PWD=$(infisical secrets get WW_VM_ADMIN_PWD --env=infrastructure --path=/windows-hosts --plain)
sshpass -p "$PWD" ssh dohertj2@10.100.0.48 hostname
6. For Claude Code specifically
Claude can shell out to infisical like any other CLI tool. To make the pointer syntax in our docs ([Infisical: homelab/<env>/<folder>/<KEY>]) directly executable, drop this one-liner alias / function in your shell rc:
secret() { # secret <env>/<path>/<KEY>
local arg=$1 env="${arg%%/*}" rest="/${arg#*/}" key="${rest##*/}" folder="${rest%/*}"
infisical secrets get "$key" --env="$env" --path="${folder:-/}" --plain
}
Now secret infrastructure/esxi/GOVC_PASSWORD returns the value, matching the doc pointer 1:1.
7. (Optional) Infisical MCP server for Claude Code
For native tool access (instead of shell-out), add an MCP server to ~/.claude/mcp.json. The community package's name and exact config can shift — check the latest before depending on it. Outline:
{
"mcpServers": {
"infisical": {
"command": "npx",
"args": ["-y", "@infisical/mcp-server"],
"env": {
"INFISICAL_HOST": "https://infisical.dohertylan.com",
"INFISICAL_CLIENT_ID": "<id>",
"INFISICAL_CLIENT_SECRET": "<secret>",
"INFISICAL_PROJECT_ID": "e36459c8-b071-4b86-a43c-795b31e75584"
}
}
}
}
Restart Claude Code and mcp__infisical__* tools will be available.
Homepage entry
Listed under the Infrastructure group on https://home.dohertylan.com — icon infisical.png (from dashboard-icons), URL https://infisical.dohertylan.com, description "Secrets Management". Added in roles/homepage/defaults/main.yml.
Risks / gotchas
ENCRYPTION_KEYis the master. Treat changes to that var as a destructive operation. The default is checked into git in the private ansiblearr repo — fine while the repo stays internal; rotate immediately if it ever goes public.- First sign-up gets admin with no out-of-band gating. Sign up immediately after the initial deploy so a passing scanner doesn't beat you to it.
- No Authelia middleware. Infisical's own auth is the only thing in front of the API — exposed via Cloudflare to the internet at
infisical.dohertylan.com. Enable Infisical SSO + MFA before storing anything sensitive. - Single-instance. Postgres + Redis run alongside the app on one box. Acceptable for a homelab; not HA. If the docker host goes down, secrets API is unavailable — plan integrations accordingly (don't make Infisical a hard dependency for things that need to recover during a docker host outage).