Skip to content

Infisical secrets management

Template

Infisical secrets management

This pattern composes Compute, Network, and Block Storage into a self-hosted secrets-management platform you run on infrastructure you control.

What this template does#

Provisions a single instance running Infisical, an open-source secrets-management platform (a self-hosted alternative to Doppler or 1Password Secrets). Your team stores API keys, database credentials, and other secrets on infrastructure you own:

  • Compute instance that runs Infisical in Docker alongside a bundled PostgreSQL and Redis (4 vCPU and 4 GiB RAM)
  • Private network, subnet, router, port, and security group; a floating IP for public access
  • A block volume mounted at /var/lib/docker, so the database and Redis data live on a volume you can grow rather than on the boot disk
  • cloud-init installs Docker Engine, brings up PostgreSQL and Redis, and prepares Infisical to start once you finish configuration

Infisical's ENCRYPTION_KEY, AUTH_SECRET, and the database password are generated on first boot and written to /opt/infisical/.env; no credential ships with this template.

Infisical has built-in login, no auth provider required#

Unlike some of the other self-hosted ops tools in this library, Infisical ships built-in email-and-password login. It serves the app and lets you sign up the first admin account as soon as you set SITE_URL and start the container, with no external identity provider to configure first.

For a lighter alternative without a Postgres and Redis dependency, OpenBao (the open-source Vault fork) covers similar ground with a different operational model: a single binary, no bundled web UI by default. This template leads with Infisical because of its built-in UI, project and environment model, and CLI/SDK ecosystem.

Parameters#

ParameterDescriptionDefault
key_nameSSH keypair name (must already exist)No default
flavor_nameInstance size (Infisical plus PostgreSQL and Redis runs on 4 vCPU / 4 GiB)s1a.medium
image_nameOperating system imageUbuntu-24.04
app_nameDisplay name prefix for resourcesinfisical
volume_sizeBlock volume size in GiB, mounted at /var/lib/docker20
external_networkExternal network for floating IP allocationPublicStatic
private_cidrCIDR for the private subnet10.47.0.0/24
app_allowed_cidrCIDR allowed to reach Infisical on port 808010.47.0.0/24

Finish setup after apply#

cloud-init starts PostgreSQL and Redis and writes the generated secrets to /opt/infisical/.env. Complete the setup over SSH:

  1. Point a domain's DNS A record at the floating IP and put a reverse proxy (Caddy or Nginx) in front for HTTPS on 443.
  2. Edit /opt/infisical/.env: set SITE_URL to your public HTTPS address.
  3. Start Infisical:
bash
cd /opt/infisical
sudo docker compose up -d

Back up ENCRYPTION_KEY#

ENCRYPTION_KEY encrypts every secret Infisical stores at rest. Losing it makes every stored secret permanently unrecoverable; there is no recovery path. Copy /opt/infisical/.env to a secure location outside this instance immediately after first boot, before you store any real secrets.

Access and security#

Infisical listens on port 8080 over plain HTTP. The security group restricts 8080 to app_allowed_cidr, which defaults to the private network only. Because Infisical needs a public URL for auth callbacks and CLI/SDK logins, the normal access path is a domain with HTTPS on 443 behind a reverse proxy. Ports 80 and 443 stay open for that proxy; they carry no traffic until you add one.

When to use this pattern#

Centralize secrets for a team or project with versioning, environments, and access control, on a host you operate. The bundled PostgreSQL and Redis suit a single-team deployment; to run them separately, point DB_CONNECTION_URI and REDIS_URL at a self-managed PostgreSQL instance and a Redis instance. The quake.yaml launch manifest declares secret names but never values; Infisical is a natural runtime secret store to inject those values from at deploy time.

Estimated cost#

Monthly cost estimate

Pricing calculator ↗

Sized as a custom package on shared vCPU.

Starting template$32.00/mo

Monthly total for the required template above. Use the configurator below to add optional pieces and see the total update.

What each resource is for

Infisical secrets-management host

s1a.medium · 4 shared vCPU, 4 GiB RAM, 0.5 Gbps

Runs Infisical in Docker (the secrets-management app) alongside its bundled PostgreSQL and Redis, with the database and Redis data on an attached volume.

Infisical plus its bundled PostgreSQL and Redis runs on 4 vCPU and 4 GiB RAM. Size up for large teams or heavy secret-access volume.

$33.00/mo

Compute shown per role at custom-package rates ($29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM). The headline above is the billed total: the cheaper of a named plan and the custom package, plus add-ons.

Included in baseline

s1a.medium

4 shared vCPU, 4 GiB RAM, 0.5 Gbps

$33.00

Compute + RAM rate basis

4 vCPU + 4 GiB RAM at $29/dedicated vCPU, $7.25/shared vCPU, $1/GiB RAM (regular). Totals apply the flat −$5/mo package promotion.

—

Block storage (50 GiB)

50 GiB at $0.08/GiB/mo

$4.00

Public IP (included)

1 included with the custom package

$0.00

Package promotional discount

Flat −$5.00/mo on the custom package (same promotion as named plans).

$-5.00

Included at no charge

These line items are zero on Quake AI. Many other providers meter them separately.

Data transfer (inbound and outbound)

Unlimited data transfer on every plan; Quake AI does not meter per-GB egress.

AWS, GCP, and Azure meter outbound transfer per GB. DigitalOcean and Hetzner include an allowance on compute plans, then charge overage.

Learn more
$0.00

Private networking

Private networks, subnets, Neutron routers, and security groups are included with the plan.

VPC objects are usually free to create elsewhere, but NAT gateways bill hourly plus per-GB processed. Quake AI uses router SNAT with no separate NAT line item.

$0.00

Control-plane API requests

OpenStack API calls for provisioning and management are included.

Some managed services on other clouds meter API calls or charge for premium control-plane features.

$0.00

Pricing data last validated: . For current rates, check quake.ai/pricing.

Template source#

7 files. Download the zip or expand to copy any file.Download infisical-secrets.zip
Show source (7 files)
main.tfHCL
data "openstack_images_image_v2" "os" {
  name        = var.image_name
  most_recent = true
}

data "openstack_networking_network_v2" "external" {
  name = var.external_network
}

resource "openstack_networking_network_v2" "private" {
  name           = "${var.app_name}-net"
  admin_state_up = true
}

resource "openstack_networking_subnet_v2" "private" {
  name            = "${var.app_name}-subnet"
  network_id      = openstack_networking_network_v2.private.id
  cidr            = var.private_cidr
  ip_version      = 4
  dns_nameservers = ["1.1.1.1", "8.8.8.8"]
}

resource "openstack_networking_router_v2" "main" {
  name                = "${var.app_name}-router"
  external_network_id = data.openstack_networking_network_v2.external.id
}

resource "openstack_networking_router_interface_v2" "private" {
  router_id = openstack_networking_router_v2.main.id
  subnet_id = openstack_networking_subnet_v2.private.id
}

resource "openstack_networking_secgroup_v2" "infisical" {
  name        = "${var.app_name}-sg"
  description = "SSH and HTTP/HTTPS for a reverse proxy; app port 8080 restricted"
}

resource "openstack_networking_secgroup_rule_v2" "ssh" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 22
  port_range_max    = 22
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.infisical.id
}

# 80 and 443 carry Infisical when it is served over a domain with automatic
# TLS through a reverse proxy (Caddy or Nginx). Infisical needs a stable
# public URL for auth callbacks and CLI/SDK access, so the domain path is the
# expected way to reach it; see the reference page.
resource "openstack_networking_secgroup_rule_v2" "http" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 80
  port_range_max    = 80
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.infisical.id
}

resource "openstack_networking_secgroup_rule_v2" "https" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 443
  port_range_max    = 443
  remote_ip_prefix  = "0.0.0.0/0"
  security_group_id = openstack_networking_secgroup_v2.infisical.id
}

# Raw app HTTP on 8080 is restricted to app_allowed_cidr (the private network
# by default). Use it for setup over an SSH tunnel or a scoped workstation IP;
# put a reverse proxy on 443 in front for routine access.
resource "openstack_networking_secgroup_rule_v2" "app" {
  direction         = "ingress"
  ethertype         = "IPv4"
  protocol          = "tcp"
  port_range_min    = 8080
  port_range_max    = 8080
  remote_ip_prefix  = var.app_allowed_cidr
  security_group_id = openstack_networking_secgroup_v2.infisical.id
}

resource "openstack_networking_port_v2" "infisical" {
  name               = "${var.app_name}-port"
  network_id         = openstack_networking_network_v2.private.id
  security_group_ids = [openstack_networking_secgroup_v2.infisical.id]

  fixed_ip {
    subnet_id = openstack_networking_subnet_v2.private.id
  }

  depends_on = [openstack_networking_router_interface_v2.private]
}

resource "openstack_blockstorage_volume_v3" "data" {
  name = "${var.app_name}-data"
  size = var.volume_size
}

resource "openstack_compute_instance_v2" "infisical" {
  name        = var.app_name
  flavor_name = var.flavor_name
  key_pair    = var.key_name

  user_data = templatefile("${path.module}/cloud-init/infisical.yaml.tftpl", {
    app_name = var.app_name
  })

  block_device {
    uuid                  = data.openstack_images_image_v2.os.id
    source_type           = "image"
    destination_type      = "volume"
    volume_size           = 30
    boot_index            = 0
    delete_on_termination = true
  }

  network {
    port = openstack_networking_port_v2.infisical.id
  }
}

resource "openstack_compute_volume_attach_v2" "data" {
  instance_id = openstack_compute_instance_v2.infisical.id
  volume_id   = openstack_blockstorage_volume_v3.data.id
}

resource "openstack_networking_floatingip_v2" "infisical" {
  pool = var.external_network
}

resource "openstack_networking_floatingip_associate_v2" "infisical" {
  floating_ip = openstack_networking_floatingip_v2.infisical.address
  port_id     = openstack_networking_port_v2.infisical.id
}
variables.tfHCL
variable "key_name" {
  description = "SSH keypair name (must already exist in your project)"
  type        = string
}

variable "flavor_name" {
  description = "Instance size. Infisical plus its bundled PostgreSQL and Redis runs comfortably on 4 vCPU and 4 GiB RAM (s1a.medium). Size up for large teams or heavy secret-access volume."
  type        = string
  default     = "s1a.medium"
}

variable "image_name" {
  description = "Operating system image. Ubuntu 24.04 is the recommended base."
  type        = string
  default     = "Ubuntu-24.04"
}

variable "app_name" {
  description = "Display name prefix for compute and network resources"
  type        = string
  default     = "infisical"
}

variable "volume_size" {
  description = "Block volume size in GiB, mounted at /var/lib/docker so the secrets-management data (the PostgreSQL database and Redis) lives on a volume you can grow rather than on the boot disk."
  type        = number
  default     = 20
}

variable "external_network" {
  description = "Shared external network for router gateway and floating IPs; defaults to PublicStatic (persisted FIP / production pattern). Override with PublicEphemeral for ephemeral demos."
  type        = string
  default     = "PublicStatic"
}

variable "private_cidr" {
  description = "CIDR for the private tenant network the instance lives in"
  type        = string
  default     = "10.47.0.0/24"
}

variable "app_allowed_cidr" {
  description = "CIDR allowed to reach Infisical on port 8080. Defaults to the private network only, so the app is not exposed to the public internet on its raw port. Infisical needs a stable public URL for auth callbacks and CLI/SDK access, so serve it over a domain with HTTPS on 443 behind a reverse proxy. To reach port 8080 directly from your workstation during setup, set this to YOUR_IP/32."
  type        = string
  default     = "10.47.0.0/24"
}
outputs.tfHCL
output "instance_id" {
  description = "ID of the compute instance running Infisical"
  value       = openstack_compute_instance_v2.infisical.id
}

output "floating_ip" {
  description = "Public floating IP address of the Infisical host"
  value       = openstack_networking_floatingip_v2.infisical.address
}

output "private_ip" {
  description = "Private IP address of the instance"
  value       = openstack_compute_instance_v2.infisical.access_ip_v4
}

output "app_url" {
  description = "Infisical app URL on port 8080. Reachable from app_allowed_cidr (the private network by default). Infisical needs a stable public URL for auth callbacks and CLI/SDK access; put a reverse proxy in front and use HTTPS on 443, then set SITE_URL in /opt/infisical/.env."
  value       = "http://${openstack_networking_floatingip_v2.infisical.address}:8080"
}
versions.tfHCL
terraform {
  required_version = ">= 1.6.0"

  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 2.0"
    }
  }
}

provider "openstack" {}
terraform.tfvars.exampleHCL
# Required: SSH keypair must already exist in your project
key_name = "YOUR_KEY_NAME"

# Recommended: restrict the app port (8080) to your workstation IP for setup.
# Leave unset to keep 8080 reachable only from the private network and tunnel
# over SSH. Infisical needs a stable public URL for auth callbacks and
# CLI/SDK access, so the normal access path is a domain with HTTPS on 443
# behind a reverse proxy.
# app_allowed_cidr = "203.0.113.10/32"

# flavor_name = "s1a.medium"
# image_name = "Ubuntu-24.04"
# app_name = "infisical"
# volume_size = 20
# external_network = "PublicStatic"
# private_cidr = "10.47.0.0/24"
cloud-init/infisical.yaml.tftpl
#cloud-config
package_update: true
packages:
  - ca-certificates
  - curl
write_files:
  - path: /opt/infisical/docker-compose.yml
    permissions: "0644"
    content: |
      # Infisical secrets management for ${app_name}. The app listens on port
      # 8080. Infisical needs a stable public URL (SITE_URL) before auth
      # callbacks and CLI/SDK logins work: set SITE_URL in
      # /opt/infisical/.env, then start the app. No credential ships with
      # this template: ENCRYPTION_KEY, AUTH_SECRET, and the database
      # password are generated on first boot. cloud-init starts PostgreSQL
      # and Redis; bring up Infisical after you finish configuring
      # /opt/infisical/.env.
      services:
        infisical:
          image: infisical/infisical:latest-postgres
          restart: unless-stopped
          ports:
            - "8080:8080"
          env_file:
            - /opt/infisical/.env
          depends_on:
            - postgres
            - redis
        postgres:
          image: postgres:16-alpine
          restart: unless-stopped
          env_file:
            - /opt/infisical/.env
          volumes:
            - infisical_pg:/var/lib/postgresql/data
        redis:
          image: redis:7-alpine
          restart: unless-stopped
          volumes:
            - infisical_redis:/data
      volumes:
        infisical_pg:
        infisical_redis:
runcmd:
  - |
    set -e
    # The data volume attaches as /dev/sdb on this platform (not /dev/vdb).
    # Mount it at /var/lib/docker before Docker is installed so the
    # PostgreSQL data and Redis live on the resizable volume rather than the
    # boot disk.
    DEV=/dev/sdb
    for i in $(seq 1 30); do [ -b "$DEV" ] && break; sleep 5; done
    if ! blkid "$DEV" >/dev/null 2>&1; then mkfs.ext4 -F -L infisicaldata "$DEV"; fi
    mkdir -p /var/lib/docker
    mount "$DEV" /var/lib/docker
    grep -q "$DEV" /etc/fstab || echo "$DEV /var/lib/docker ext4 defaults,nofail 0 2" >> /etc/fstab
    # Install Docker Engine plus the compose plugin from Docker's convenience
    # script.
    curl -fsSL https://get.docker.com | sh
    # Generate Infisical's encryption key, auth secret, and the bundled
    # database password on first boot. These never leave this instance.
    # ENCRYPTION_KEY decrypts every secret Infisical stores: back it up
    # somewhere safe outside this instance. Losing it makes every stored
    # secret unrecoverable.
    ENCRYPTION_KEY=$(openssl rand -hex 16)
    AUTH_SECRET=$(openssl rand -base64 32)
    PGPASS=$(openssl rand -hex 24)
    umask 077
    {
      echo "ENCRYPTION_KEY=$ENCRYPTION_KEY"
      echo "AUTH_SECRET=$AUTH_SECRET"
      echo "DB_CONNECTION_URI=postgres://infisical:$PGPASS@postgres:5432/infisical"
      echo "POSTGRES_USER=infisical"
      echo "POSTGRES_PASSWORD=$PGPASS"
      echo "POSTGRES_DB=infisical"
      echo "REDIS_URL=redis://redis:6379"
      echo "PORT=8080"
      echo "# Set SITE_URL to your public HTTPS address before starting Infisical:"
      echo "# SITE_URL=https://secrets.example.com"
    } > /opt/infisical/.env
    chmod 600 /opt/infisical/.env
    # Bring up the datastores. Infisical starts after you set SITE_URL in
    # /opt/infisical/.env and run:
    #   cd /opt/infisical && docker compose up -d
    cd /opt/infisical
    docker compose up -d postgres redis
README.mdMarkdown
# Infisical secrets management

Single compute instance running [Infisical](https://infisical.com), a self-hosted secrets-management platform (a self-hosted alternative to Doppler or 1Password Secrets) on infrastructure you control. After apply, you set a public URL, start the app, sign up the first admin account, and create a project, an environment, and your first secret.


**Network class:** production — `external_network` defaults to `PublicStatic` for persisted floating IPs and multi-tier stacks; override with `PublicEphemeral` for ephemeral demos.

The instance provisions a private network, a floating IP, and a block volume mounted at `/var/lib/docker` so the secrets-management data lives on a resizable volume. cloud-init installs Docker Engine, brings up the bundled PostgreSQL and Redis, and prepares Infisical to start once you finish configuration.

## Where this fits

Infisical centralizes API keys, database credentials, and other secrets for a team or project, with versioning, environments, and access control, on infrastructure you own rather than on a third-party SaaS. Unlike some of the other self-hosted ops tools in this library, Infisical ships built-in email-and-password login: it has no external identity-provider requirement.

## A lighter or heavier alternative

If you want a simpler secrets store without a Postgres and Redis dependency, [OpenBao](https://openbao.org) (the open-source Vault fork) covers a similar scope with a different operational model (a single binary, no bundled web UI by default). This template leads with Infisical because of its built-in UI, project/environment model, and CLI/SDK ecosystem.

## Prerequisites

- OpenTofu >= 1.6.0 or Terraform >= 1.6.0
- Quake AI account with OpenStack credentials
- An existing SSH keypair in your project (the value of `key_name` must match that keypair)
- A domain you can point at the instance (Infisical needs a stable public URL for auth callbacks and CLI/SDK access)

## Resource baseline

Infisical plus its bundled PostgreSQL and Redis runs on 4 vCPU and 4 GiB RAM. The default `s1a.medium` flavor leaves headroom for the three containers. Size up for large teams or heavy secret-access volume.

## Usage

1. Clone or copy this template directory
2. Copy `terraform.tfvars.example` to `terraform.tfvars` and fill in your values
3. Source your OpenStack credentials: `source openrc.sh`
4. Initialize: `tofu init`
5. Preview: `tofu plan`
6. Apply: `tofu apply`

After apply, cloud-init installs Docker, generates Infisical's `ENCRYPTION_KEY`, `AUTH_SECRET`, and the database password into `/opt/infisical/.env`, and starts PostgreSQL and Redis. Finish the configuration over SSH:

1. Point a domain's DNS A record at `floating_ip` and put a reverse proxy (Caddy or Nginx) in front for HTTPS on 443.
2. Edit `/opt/infisical/.env`: set `SITE_URL` to your public HTTPS address.
3. Start Infisical:

```bash
cd /opt/infisical
sudo docker compose up -d
```

No credential ships with this template: `ENCRYPTION_KEY`, `AUTH_SECRET`, and the database password are generated on first boot.

## Back up ENCRYPTION_KEY

`ENCRYPTION_KEY` encrypts every secret Infisical stores at rest. If you lose it, every stored secret becomes permanently unrecoverable: there is no recovery path. Copy `/opt/infisical/.env` to a secure location outside this instance (a password manager or an offline backup) immediately after first boot, before you store any real secrets.

## Access and security

Infisical listens on port 8080 over plain HTTP. The security group restricts 8080 to `app_allowed_cidr`, which defaults to the private network only. Because Infisical needs a public URL for auth callbacks and CLI/SDK logins, the normal access path is a domain with HTTPS on 443 behind a reverse proxy. Point the domain's DNS A record at `floating_ip`. Ports 80 and 443 stay open for that reverse proxy; they carry no traffic until you add one.

## Datastores

This template bundles PostgreSQL and Redis as containers on the same instance, which suits a single-team secrets store. To run them as separate services, point `DB_CONNECTION_URI` and `REDIS_URL` in `/opt/infisical/.env` at a [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres) instance and a [Redis](/resources/iac-templates/redis-cache) instance, and remove the bundled services from the compose file.

## Variables

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `key_name` | string | yes | n/a | SSH keypair name (must already exist in your project) |
| `flavor_name` | string | no | `s1a.medium` | Instance size (Infisical plus PostgreSQL and Redis runs on 4 vCPU / 4 GiB) |
| `image_name` | string | no | `Ubuntu-24.04` | Operating system image |
| `app_name` | string | no | `infisical` | Display name prefix for resources |
| `volume_size` | number | no | `20` | Block volume size in GiB, mounted at `/var/lib/docker` |
| `external_network` | string | no | `PublicStatic` | Persisted FIP / production default; override with `PublicEphemeral` for demos |
| `private_cidr` | string | no | `10.47.0.0/24` | CIDR for the private subnet |
| `app_allowed_cidr` | string | no | `10.47.0.0/24` | CIDR allowed to reach Infisical on port 8080 |

## Outputs

| Name | Description |
| --- | --- |
| `floating_ip` | Public floating IP assigned to the instance |
| `private_ip` | Private IP address of the instance |
| `app_url` | Infisical app URL on port 8080 |
| `instance_id` | Compute instance ID |

## Scope

This is a single-VM Infisical host that you operate, not a managed secrets cloud. It is CPU-only and runs in one region, and it bundles PostgreSQL and Redis as containers on the same host. You operate the instance, Docker, Infisical, the database, Redis, and the data volume yourself: back them up (`ENCRYPTION_KEY` especially), patch them, and watch resource use as the team grows. For a larger team, move PostgreSQL and Redis onto their own instances and size the app host up.

## Documentation

See also: [self-managed PostgreSQL](/resources/iac-templates/self-managed-postgres), [Redis](/resources/iac-templates/redis-cache)
Resources, parameters, and variables
Provisions
Parameterized by
Variables
  • key_namerequired
  • flavor_name="s1a.medium"
  • image_name="Ubuntu-24.04"
  • app_name="infisical"
  • volume_size=20
  • external_network="PublicStatic"
  • private_cidr="10.47.0.0/24"
  • app_allowed_cidr="10.47.0.0/24"

Customize this pattern#

See also#

Usage Guidelines

The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.

For the full policy, see Usage Guidelines.

Was this page helpful?