Skip to main content
Bloom’s production infrastructure is managed as code using OpenTofu, a drop-in replacement for Terraform maintained by the Cloud Native Computing Foundation. The infrastructure code lives in the infra/ directory and targets AWS as the primary cloud provider. Frontend sites can also be deployed to Netlify.

Infrastructure-as-code overview

Rather than clicking through the AWS console or writing one-off shell scripts, Bloom uses OpenTofu .tf files to describe all required resources declaratively. Running tofu apply computes the difference between the current state of your AWS account and the desired configuration, then applies only the necessary changes.
The infra/ codebase is compatible with both OpenTofu and Terraform.

Directory structure

infra/
├── aws_deployment_guide/          # Step-by-step AWS deployment guide
│   ├── 0_README.md
│   ├── 1_create_aws_accounts.md
│   ├── 2_iam_identity_center_configuration.md
│   ├── 3_create_tofu_state_s3_bucket.md
│   ├── 4_fork_bloom_repo.md
│   ├── 5_apply_deployer_permission_set_tofu_modules.md
│   ├── 6_apply_bloom_deployment_tofu_modules.md
│   └── 7_operations_playbook.md
├── tofu_importable_modules/       # Reusable, parameterized resource modules
│   ├── bloom_deployer_permission_set_policy/
│   └── bloom_deployment/
├── tofu_root_modules/             # Root modules (maintained in forks)
├── Dockerfile                     # Production infra container
├── Dockerfile.dev                 # Development infra container
├── docker-entrypoint.py           # Entrypoint for the infra-dev container
└── DEVELOPMENT.md                 # Guide for developing the OpenTofu modules
tofu_root_modules/ is maintained in forks, not in the core Bloom repo. See the bloom-exygy repository for an example structure.

OpenTofu modules

bloom_deployment

The primary importable module. Configures all AWS resources required for a complete Bloom deployment in a single AWS account:
Terraform fileResources configured
vpc.tfVPC, subnets, routing
lb.tfApplication Load Balancer
ecs.tfECS cluster
ecs_api_service.tfECS service for the Bloom API (1 vCPU, 2 GiB)
ecs_site_public_service.tfECS service for the public site (2 vCPU, 6 GiB)
ecs_site_partners_service.tfECS service for the partners site (2 vCPU, 4 GiB)
ecs_dbinit_task.tfOne-off ECS task for DB initialization (0.25 vCPU, 512 MiB)
ecs_dbseed_task.tfOne-off ECS task for DB seeding (1 vCPU, 4 GiB)
db.tfRDS PostgreSQL instance
s3.tfS3 buckets
secrets.tfAWS Secrets Manager secrets
ses.tfSES email configuration

bloom_deployer_permission_set_policy

Configures the IAM Identity Center permission set policy with the minimum permissions required to deploy Bloom. Used to grant the deployer role access without over-provisioning.

AWS deployment guide

The infra/aws_deployment_guide/ directory contains a numbered, step-by-step guide for standing up Bloom in a new AWS organization. Follow the files in order:
1

Create AWS accounts

Set up the required AWS account structure for your organization (1_create_aws_accounts.md).
2

Configure IAM Identity Center

Set up SSO and IAM Identity Center for secure, federated access (2_iam_identity_center_configuration.md).
3

Create Tofu state S3 bucket

Provision the S3 bucket that stores OpenTofu remote state (3_create_tofu_state_s3_bucket.md).
4

Fork the Bloom repo

Fork the repository to maintain your own root modules and environment-specific configuration (4_fork_bloom_repo.md).
5

Apply deployer permission set modules

Apply the bloom_deployer_permission_set_policy module to configure the deployer IAM role (5_apply_deployer_permission_set_tofu_modules.md).
6

Apply bloom deployment modules

Apply the bloom_deployment module to provision all application infrastructure (6_apply_bloom_deployment_tofu_modules.md).
An operations playbook (7_operations_playbook.md) covers day-two tasks such as rotating secrets and scaling services.

Running OpenTofu

Bloom provides a pre-built infra-dev container image (ghcr.io/bloom-housing/bloom/infra-dev) that includes all required tools: OpenTofu, AWS CLI, bash, and openssl.

Using the infra-dev container

Mount the infra/ directory and your AWS credentials into the container:
docker container run --rm -it \
  --user "$(id -u):$(id -g)" \
  -v ./infra:/bloom/infra:z \
  -v "${HOME}/.aws/cli":/home/.aws/cli:z \
  -v "${HOME}/.aws/sso/cache":/home/.aws/sso/cache:z \
  ghcr.io/bloom-housing/bloom/infra-dev \
  [[--skip-sso] | [--skip-init]] <ROOT_MODULE_NAME> <OPEN_TOFU_ARGS>
The container entrypoint automatically:
  1. Runs aws sso login for the selected root module’s profile (unless --skip-sso / -ss is passed).
  2. Runs tofu init to download provider dependencies (unless --skip-init / -si is passed).
  3. Passes remaining arguments directly to the tofu binary.
Add an alias for convenience (run from the root of the Bloom repo):
alias bloomtofu="docker container run --rm -it \
  --user $(id -u):$(id -g) \
  -v ${PWD}/infra:/bloom/infra:z \
  -v ${HOME}/.aws/cli:/home/.aws/cli:z \
  -v ${HOME}/.aws/sso/cache:/home/.aws/sso/cache:z \
  ghcr.io/bloom-housing/bloom/infra-dev"

# Example: apply the bloom_dev root module
bloomtofu -ss -si bloom_dev apply

Common OpenTofu commands

# Initialize providers (first run or when adding providers)
tofu init

# Update a provider version and refresh the lock file
tofu init -upgrade

# Lock provider hashes for all supported platforms
tofu providers lock \
  -platform=linux_amd64 \
  -platform=linux_arm64 \
  -platform=darwin_amd64 \
  -platform=darwin_arm64

# Preview and apply changes
tofu apply

# Destroy a specific module (e.g., for testing)
tofu destroy -target=module.bloom_deployment

# Force replacement of a specific resource
tofu apply -replace=module.bloom_deployment.aws_secretsmanager_secret.api_jwt_signing_key
Apply operations require approval. Use the -it flag when running the infra-dev container so you can interact with the approval prompt.

Required tools (local development)

If you prefer to run OpenTofu outside the container, install the following:

Netlify deployment (frontend sites)

The sites/public and sites/partners directories each include a netlify.toml configuration file for deploying the Next.js frontend sites to Netlify. Netlify provides:
  • Automated builds on branch push
  • Preview deployments for pull requests
  • Edge CDN distribution
Configure environment variables in the Netlify project settings to match the values expected by each Next.js app (e.g., BACKEND_API_BASE, JURISDICTION_NAME, LANGUAGES).

Architecture considerations

ECS Fargate

All application containers (API, public, partners) run as ECS Fargate tasks. Container resource limits in docker-compose.yml are kept in sync with the corresponding ECS task definitions.

Load balancer

An Application Load Balancer routes traffic to ECS services. Mirrors the nginx lb container used locally.

RDS PostgreSQL

The database runs on RDS. SSL is enabled in production (unlike the local DB_NO_SSL=TRUE setting).

Secrets Manager

Sensitive values such as APP_SECRET and API keys are stored in AWS Secrets Manager and injected into ECS tasks at runtime.

CI validation

The infra_ci.yml GitHub Actions workflow runs on every push to main and on pull requests that touch infra/**. It verifies that all .tf files are correctly formatted using tofu fmt -check -diff -recursive.

Build docs developers (and LLMs) love