Skip to Content

Production Guide: Deploy Infisical with Docker Compose + Caddy + PostgreSQL + Redis on Ubuntu

A production-ready secrets management platform with automated rotation, access control, and encrypted backups.

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

Talk to us

If you want this implemented with hardened defaults, observability, and tested recovery playbooks, our team can help.

Contact Us

Header image: Original SysBrix generated header, no watermark.

Production Guide: Deploy n8n with Docker Compose + Caddy + PostgreSQL + Redis on Ubuntu
Workflow automation deployed with Docker Compose, Caddy, PostgreSQL, and Redis ยท SysBrix Guides