Hard-coded secrets in repositories are not a configuration mistake; they are an operational liability. When API keys, database passwords, and cloud credentials live in environment files that get copied between laptops, CI pipelines, and staging servers, the attack surface expands with every new team member. Rotation becomes a manual chore, audit trails are fragmentary, and a single leaked key can cascade across multiple services before anyone notices.
Infisical is an open-source secrets management platform that gives engineering teams a centralized vault with role-based access, automatic rotation, versioning, and audit logging. Unlike cloud-native vaults that lock you into a provider, Infisical can run entirely on your own infrastructure, integrate with existing identity providers, and inject secrets into applications at runtime without ever writing them to disk in plaintext. This guide walks through a production deployment on Ubuntu using Docker Compose, Caddy as a reverse proxy with automatic TLS, PostgreSQL for persistence, and Redis for caching and queueing.
Architecture and flow overview
The stack consists of four primary services. Caddy terminates TLS and forwards traffic to the Infisical web application. Infisical itself is a Node.js and TypeScript application that exposes a REST API, a web dashboard, and command-line integration. PostgreSQL stores encrypted secrets metadata, project settings, user identities, and audit logs. Redis handles background job queuing, rate-limiting counters, and short-lived session cache. Backups target a local encrypted archive that is synced to an S3-compatible object store on a nightly schedule.
All services communicate over an internal Docker network. Caddy is the only container with published ports. Database credentials and the Infisical master encryption key are injected via environment files that are created before the first compose up and are never checked into version control. The architecture is single-node but stateless enough that restoring onto a replacement server requires only the latest database dump, the Redis persistence file, and the environment configuration.
Prerequisites
- Ubuntu 22.04 LTS or 24.04 LTS server with at least 2 vCPU, 4 GB RAM, and 20 GB SSD.
- A domain or subdomain pointing to the server IP (for example, secrets.example.com).
- Docker Engine 24.x and Docker Compose plugin installed.
- UFW or cloud security group allowing TCP 22, 80, and 443.
- An S3-compatible bucket or secondary server for off-site backups.
Step-by-step deployment
1. Create the project structure
sudo mkdir -p /opt/infisical/{data/postgres,data/redis,backups,caddy}
sudo chown -R $USER:$USER /opt/infisical
cd /opt/infisical
2. Write the environment file
Generate a strong encryption key and database password before creating the file.
ENCRYPTION_KEY=$(openssl rand -hex 32)
DB_PASSWORD=$(openssl rand -base64 32)
REDIS_PASSWORD=$(openssl rand -base64 32)
cat > /opt/infisical/.env <
3. Create the Docker Compose manifest
cat > /opt/infisical/docker-compose.yml <<'COMPOSE'
services:
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- infisical
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: infisical
POSTGRES_USER: infisical
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- ./data/postgres:/var/lib/postgresql/data
networks:
- infisical
healthcheck:
test: ["CMD-SHELL", "pg_isready -U infisical -d infisical"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- ./data/redis:/data
networks:
- infisical
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 5s
retries: 5
infisical:
image: infisical/infisical:latest-postgres
restart: unless-stopped
env_file: .env
environment:
DB_CONNECTION: postgres
DB_HOST: postgres
DB_PORT: 5432
DB_NAME: infisical
DB_USER: infisical
DB_PASSWORD: ${DB_PASSWORD}
REDIS_URL: redis://default:${REDIS_PASSWORD}@redis:6379
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
networks:
- infisical
volumes:
caddy_data:
caddy_config:
networks:
infisical:
driver: bridge
COMPOSE
4. Configure Caddy
cat > /opt/infisical/Caddyfile <<'CADDY'
secrets.example.com {
reverse_proxy infisical:8080
encode gzip
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
CADDY
5. Start the services
cd /opt/infisical
docker compose up -d
sleep 15
docker compose ps
docker compose logs --tail 30 infisical
6. Run database migrations
Infisical requires an initial schema migration before the first login.
docker compose exec infisical npm run migration:latest
Configuration and secrets handling
The .env file is the only place where raw secrets exist on the host. Protect it with strict permissions and avoid committing it to Git. For team access, store the file in an existing password manager or encrypted backup, and rotate the ENCRYPTION_KEY only during a planned re-encryption window because it protects all stored secrets at rest.
For SMTP, use a provider that supports application-specific passwords rather than your primary account password. If you use a cloud mail relay such as Amazon SES or Mailgun, whitelist the server IP in the provider console before starting Infisical, or invitation and alert emails will silently queue.
Inside Infisical, create an organization, then a project. Within the project, define environments such as staging and production. Import existing .env files using the CLI so that secrets move into versioned, encrypted storage instead of flat files. Enable secret rotation for supported integrations such as AWS IAM or database credentials, and set notification channels so the team receives alerts when a rotation fails or an access request is pending.
Verification
- Open https://secrets.example.com and register the first admin account. If the page loads with a valid TLS certificate, Caddy and Infisical are communicating correctly.
- Create a test secret in a project, then retrieve it with the CLI after logging in:
infisical login
infisical secrets get TEST_KEY --env=staging
- Check database connectivity from the Infisical container:
docker compose exec infisical sh -c 'node -e "require(\"pg\").Client({connectionString:process.env.DB_CONNECTION_URL}).connect().then(c=>c.query(\"SELECT 1\")).then(r=>console.log(r.rows)).catch(e=>console.error(e))"'
- Verify Redis is accepting authenticated connections:
docker compose exec redis redis-cli -a "$(grep REDIS_URL /opt/infisical/.env | cut -d: -f3 | cut -d@ -f1)" ping
- Confirm backup script output by running a dry-run of the nightly PostgreSQL dump:
docker compose exec postgres pg_dump -U infisical -d infisical > /opt/infisical/backups/test.sql
ls -lh /opt/infisical/backups/test.sql
Common issues and fixes
Migrations fail with a connection refused error. The Infisical container may start before PostgreSQL is ready to accept connections even though the healthcheck exists. Run docker compose down followed by docker compose up -d and then manually trigger the migration again. In persistent cases, add a startup sleep script or use a wait-for-it sidecar.
Emails are not sent after SMTP configuration. Verify that the SMTP provider allows outbound relay from the server IP and that the port is not blocked by UFW. Check the Infisical logs for authentication errors, and test credentials separately with swaks or a small Python script before blaming the application.
Redis authentication errors appear in logs. If the Redis password contains special characters that break the Redis URL format, percent-encode them in the .env file or regenerate a password containing only alphanumeric characters.
Certificate provisioning fails. Ensure that port 80 is reachable from the internet on the exact domain name, because Caddy uses HTTP-01 validation. If you are behind a CDN or proxy, disable it temporarily or switch to DNS-01 validation.
Secrets are visible in process listings. Never pass secrets directly on the docker compose command line. Always use the env_file directive or Docker secrets in a Swarm context. Audit running containers with docker inspect to confirm that sensitive values are not exposed in plain command arrays.
FAQ
How does Infisical encrypt secrets at rest?
Infisical uses AES-256-GCM with the ENCRYPTION_KEY you provide during installation. Each secret is encrypted before it reaches PostgreSQL, and the encryption key never leaves the application environment. Rotating the key requires re-encrypting every secret, so plan key rotation during a maintenance window.
Can I run Infisical without Redis?
Redis is optional for small deployments, but strongly recommended for production. It powers the background job queue, rate limiting, and session cache. Without Redis, Infisical falls back to in-memory storage, which breaks stateful behavior across container restarts and prevents horizontal scaling.
What is the best way to back up the vault?
Back up PostgreSQL with pg_dump on a nightly cron job, copy the dump to an S3-compatible object store, and test a restore onto a staging instance monthly. Keep the .env file and encryption key in a separate password manager or hardware security module; without the key, the database dump is unrecoverable.
How do I migrate secrets from an existing .env file?
Install the Infisical CLI, run infisical login, then use infisical secrets set --file=.env from your project directory. The CLI pushes each key-value pair into the selected project and environment, preserving the original values while adding versioning and audit history.
Does Infisical support single sign-on?
Yes. Infisical supports SAML 2.0 and OIDC providers including Okta, Google Workspace, Azure AD, and Keycloak. Configure the integration in the organization settings, map groups to projects, and enforce SSO-only logins to prevent local credential sprawl.
How do I rotate a compromised secret automatically?
Enable native integrations for supported providers such as AWS, PostgreSQL, or SendGrid. Define a rotation interval or trigger rotation manually from the secret detail page. Infisical updates the value in the vault, pushes the new value to connected services, and logs the rotation event in the audit trail.
Can I use Infisical in CI/CD pipelines without exposing credentials?
Yes. Use the Infisical CLI in injection mode, which loads secrets as environment variables at runtime without persisting them to disk. Combine this with short-lived machine identities so that CI jobs authenticate using tokens that expire after the build completes.
Internal links
- Production Guide: Deploy OpenBao with Docker Compose + Caddy + Integrated Raft on Ubuntu
- Production Guide: Deploy Passbolt with Docker Compose + Caddy + MariaDB + GnuPG on Ubuntu
- Production Guide: Deploy NetBox with Docker Compose + Caddy + PostgreSQL + Redis on Ubuntu
Talk to us
If you want this implemented with hardened defaults, observability, and tested recovery playbooks, our team can help.
Header image: Original SysBrix generated header, no watermark.