Prerequisites
- AWS account(s): Can use separate accounts for development, staging, and production
- Terraform 1.2+: For infrastructure provisioning
- Values from Reducto (provided during onboarding):
- ExternalId for secure role assumption
- Endpoint Service name and region (if using PrivateLink, also listed below)
- If using PrivateLink: Send Reducto every AWS account ID that will create a VPC endpoint, including separate dev, staging, production, or organizational accounts. Reducto must allow-list each account on the VPC Endpoint Service before endpoint creation succeeds.
Reducto regions
Reducto runs separate deployments in the US, EU, and AU. Each deployment uses only buckets in its own area and assumes your IAM role with its own principals. Find the Reducto region for your bucket’s AWS region, then use that row’s principal ARNs and API base URL.
Buckets in other AWS regions are not supported.
Principal ARNs
Trust all three ARNs for the Reducto region that serves your bucket:Trust both Modal ARNs for your region. Reducto’s Modal compute assumes your role either as the IAM user or as the IAM role of the same name. Omitting the role ARN causes
AccessDenied on sts:AssumeRole for jobs that finalize on Modal.VPC Endpoint Service Configuration
If using PrivateLink, use the endpoint service for the Reducto region that serves your bucket, and create your VPC endpoint in one of its supported VPC regions:If your VPC is in a different region from the service region, the endpoint connects cross-region. Set
reducto_endpoint_service_region to the service region. To connect from a VPC region that is not listed, contact Reducto.Setup
1
Clone the infrastructure repository
2
Create terraform.tfvars
Use the principal ARNs for the Reducto region that serves your bucket:Optional variables:
- US
- EU
- AU
3
Initialize and apply
The root module has no provider block, so Terraform creates the bucket in the region from your AWS environment. Set it to your bucket’s region:
4
Share outputs with Reducto
Components provisioned
Access Mode
Keep the default,access_mode = "assume_role". Reducto reaches your bucket by assuming the IAM role with your ExternalId.
PrivateLink Setup (Optional)
For private-only API access without traversing the public internet:1
Request PrivateLink enablement
Provide the following to your Reducto team:
- AWS Account ID(s): Every account where you’ll create a VPC endpoint, including dev, staging, production, or separate organizational accounts
- Region(s): The AWS regions of the VPCs that will connect, and the Reducto region that serves your bucket
2
Configure VPC endpoint
Add to your The module creates a security group that allows HTTPS from the VPC’s primary CIDR. If clients connect from other networks, such as peered VPCs, Transit Gateway, or VPN, pass your own security groups in
terraform.tfvars, using the service name and service region for your Reducto region from the table above:privatelink_security_group_ids.3
Configure your client
Use the DNS name for your Reducto region:
Require PrivateLink
By default, your organization can also call the public API base URL. Ask Reducto to enable PrivateLink-only access for your organization. Reducto then rejects requests that do not arrive through PrivateLink with HTTP 403.Validation Checklist
Afterterraform apply, verify your setup:
- Terraform apply succeeded without errors
- S3 bucket has lifecycle rule:
- S3 bucket blocks public access: All four settings are
true - IAM role trust policy is correct: The principals are all three ARNs for your Reducto region, and the
sts:ExternalIdcondition matches the ExternalId from Reducto - If PrivateLink enabled: Reducto has confirmed that every endpoint-creating AWS account is allow-listed
- If PrivateLink enabled: Endpoint status shows “available”
- Smoke test: After Reducto confirms the setup, run a small job against your Reducto region’s API base URL and verify objects appear in the bucket
Troubleshooting
AccessDenied on sts:AssumeRole
Problem: Jobs fail because Reducto getsAccessDenied when it assumes your role.
Solution: Check the trust policy with the aws iam get-role command from the Validation Checklist. The common causes are:
- Wrong region’s principals: The role trusts the US ARNs, but your bucket is served by EU or AU (or the reverse). Set
reducto_principal_arnsto the Principal ARNs row for your bucket’s Reducto region and runterraform applyagain. - Missing Modal role ARN: The role trusts
user/modal-*but notrole/modal-*. Add the role ARN for your region. - ExternalId mismatch: The
sts:ExternalIdvalue in the trust policy must exactly match the ExternalId Reducto registered for your organization. - Role ARN mismatch: The role ARN Reducto registered must match the role you created. The module names it
<name_prefix>-reducto-access.
AccessDenied on S3 after the role is assumed
Problem: Reducto assumes the role, but reads or writes fail withAccessDenied.
Solution: Check that no bucket policy denies the role. If the bucket uses SSE-KMS with a customer managed key, grant the KMS permissions in Encryption. To read s3:// inputs from a bucket other than the Reducto bucket, add s3:GetObject on that bucket to the role’s policy.
Region cannot be used with this deployment
Problem: The API returns an error that your Hybrid VPC region or environment cannot be used with this deployment, or that your organization is not configured for this region. Solution: Call the API base URL for the Reducto region that serves your bucket (see Reducto regions). With PrivateLink, use that region’s endpoint service and DNS name.VPC endpoint service does not exist
Problem: AWS returnsInvalidServiceName or says the VPC Endpoint Service does not exist, but the service name and region match the table above.
Solution: Send Reducto the AWS account ID for the account creating the endpoint. Reducto will allow-list the account root on the endpoint service. After Reducto confirms the change, retry endpoint creation. Repeat this for each account that will create an endpoint. Also confirm that your VPC’s region is one of the service’s supported VPC regions.
Multi-Region Setup
Deploy one bucket and IAM role per region. Each role must trust the principals of the Reducto region that serves its bucket. Give each region its ownname_prefix, because the IAM role name (<name_prefix>-reducto-access) must be unique in an AWS account.
Copy the minimal example into one directory per region. Each directory keeps its own Terraform state:
terraform.tfvars in each directory with that region’s principal ARNs:
integration_values output:
https://au.platform.reducto.ai reads and writes your ap-southeast-2 bucket.
Multi-Environment Setup
For organizations with separate AWS accounts for dev/staging/prod, create one directory per environment underexamples/multi-environment/, and copy main.tf and terraform.tfvars.example (renamed to terraform.tfvars) into each. The template’s module source (../../../) expects this location:
Multiple Buckets for One Reducto Org
For workflows that need client-specific buckets under one Reducto organization, Reducto registers each bucket/role pair as a named environment:region must belong to the Reducto region you call. For environments in more than one Reducto region, Reducto can set a default per region with us_default_environment, eu_default_environment, and au_default_environment. If your organization has several environments, set a default or name an environment on every request. Without a default, a request that names no environment either fails with an error that lists the available environments or, if Reducto also registered regional storage for your organization, uses that storage.
Select the environment on each request. Where the setting goes depends on the endpoint:
- Parse
- Extract and Split
- Upload
s3:// inputs with the selected environment’s role, so that role needs s3:GetObject on the input bucket.
Security
ExternalId protection
The ExternalId in the IAM role trust policy prevents confused deputy attacks. Only requests with the correct ExternalId can assume the role.Role sessions
Reducto assumes the role with the session namereducto-hybrid-vpc and a one-hour session that it renews automatically. CloudTrail in your account records these AssumeRole calls.
URLs that Reducto returns for objects in your bucket, such as URL results and images, are presigned with this session. They stop working when the session expires, within one hour, so download them promptly.
Principle of least privilege
The IAM role grants only the permissions necessary for Reducto operations:s3:GetObject: read documentss3:PutObject: write results and artifactss3:DeleteObject: delete objects, for example when you delete a jobs3:ListBucket,s3:GetBucketLocation: bucket-level accesss3:AbortMultipartUpload,s3:ListMultipartUploadParts,s3:ListBucketMultipartUploads: handle large file uploads
Encryption
The module turns on default encryption with SSE-S3 (AES256). If you switch the bucket to SSE-KMS with a customer managed key, add kms:Decrypt and kms:GenerateDataKey on that key to the role’s policy, and allow the role in the key policy. To have Reducto request that key on every write, send Reducto the key ARN.