Customer-facing inputs for the ECS Terraform module. Internal/debug and
Runlayer-fleet-only variables are omitted. Examples and defaults are
placeholders — do not paste real API keys, customer names, or
account-specific identifiers. See the
ECS + Terraform guide for deploy steps and examples.
For corporate egress filters / SWG allowlists, see
Self-Hosted Egress Requirements.
Requirements
| Name | Version |
|---|---|
| Terraform / OpenTofu | >= 1.11.0 (see short-circuit note below) |
hashicorp/aws | >= 6.64.0, < 7.0.0 |
hashicorp/random | ~> 3.9 |
aws and aws.route53.
AWS provider 6.64.0 adds the alarm warm-up used by ToolGuard registration monitoring.
When upgrading this module, upgrade any older AWS provider selection in your
dependency lock file before planning.
Some optional list validations use forms like
var.x == null || length(var.x) > 0.
Terraform before 1.12 does not short-circuit boolean ||, so leaving the variable
unset/null can fail at plan with length(null). Prefer Terraform >= 1.12 or
OpenTofu >= 1.11 (the module minimum). Omitting an argument whose default is
null does not avoid this issue on older Terraform versions.Naming constraints
| Input | Constraint |
|---|---|
project | 1–10 chars; ^[a-zA-Z0-9-]+$ |
environment | production | staging | development |
Core
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
region | AWS region | string | n/a (required) | yes |
domain_name | Domain name for the application (required) Validation: Domain name is required for all deployments. | string | n/a (required) | yes |
account | AWS account ID | string | n/a (required) | yes |
app_version | Application version used to build default ECR image tags (mutually exclusive with ecr_repositories). Validation: app_version cannot be an empty string.; Provide either app_version or ecr_repositories, not both. | string | null | no |
ecr_repositories | Map of service names to their ECR repository URIs (backend, worker, frontend required). Overrides the module defaults. Validation: ecr_repositories cannot be an empty map. Provide image URIs for all services or omit to use defaults.; ECR repository URIs must be either public ECR (public.ecr.aws/namespace/repo:tag) or private ECR (account.dkr.ecr.region.amazonaws.com/repo:tag) format; Each ECR repository URI must include an explicit image tag or digest (no implicit latest).; ecr_repositories must include backend, frontend, and worker entries. | map(string) | null | no |
project | Project name Validation: Project name must be between 1 and 10 characters.; Project name must contain only alphanumeric characters and hyphens. | string | "anysource" | no |
environment | Environment (production, staging, development) Validation: Environment must be one of: production, staging, development | string | "production" | no |
customer_id | Customer identifier for this deployment (defaults to domain name for telemetry tagging) | string | "" | no |
deployment_name | Human-readable deployment name for telemetry (e.g. production, staging, sandbox-<dev>); defaults to environment when empty | string | "" | no |
infra_version | Infrastructure version for tracking deployment changes in Sentry (defaults to app image tag when not provided) | string | "" | no |
additional_tags | Additional tags for deployment workload resources (see README coverage). Empty by default; partner attribution requires explicit caller opt-in. Resource-specific tags take precedence. Validation: The aws-apn-id tag must use pc:<Marketplace product code>. Verify the code and resource ownership before enabling attribution.; additional_tags must not replace a different aws-apn-id from provider default_tags. Resolve attribution ownership first. | map(string) | {} | no |
deletion_protection | Enable deletion protection for RDS clusters | bool | true | no |
Auth & catalog
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
auth_client_id | Auth client ID for authentication. This will be provided by the Runlayer team. Validation: auth_client_id must not be empty. Ask Runlayer support for your auth_client_id. | string | n/a (required) | yes |
auth_api_key | Auth API key for authentication. This will be provided by the Runlayer team. Validation: auth_api_key must not be empty. Ask Runlayer support for your auth_api_key. (sensitive) | string | n/a (required) | yes |
directory_sync_enabled | Deprecated. Mapped onto DIRECTORY_SYNC_EVENT_SYNC_ENABLED for compatibility; event sync now runs on the worker service. | bool | true | no |
directory_sync_interval_minutes | Deprecated. Mapped onto DIRECTORY_SYNC_EVENT_CRON for compatibility (exact below 60 minutes, hourly-approximate above). Validation: Directory sync interval must be between 1 and 1440 minutes (24 hours) | number | 10 | no |
mcp_catalog_api_url | URL of the MCP Catalog API service. If empty, backend uses default. | string | "" | no |
mcp_catalog_api_key | API key for MCP Catalog API authentication (sensitive) | string | (sensitive — omitted) | no |
VPC & networking
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
vpc_cidr | VPC CIDR. Still required to match the real VPC CIDR when using existing_vpc_id (default 10.0.0.0/16); used for security-group / allowlist CIDR rules even when the module does not create the VPC. | string | "10.0.0.0/16" | no |
region_az | Availability zones for the main VPC and RDS. Empty picks the first three in the region. It also places a module-created AgentCore consumer VPC, which otherwise uses the AZs whose zone IDs AgentCore supports; the plan fails if a listed AZ is not supported there. | list(string) | [] | no |
single_nat_gateway | Use a single NAT Gateway instead of one per AZ (cost savings for non-prod) | bool | false | no |
enable_ipv6 | Enable IPv6 networking for managed VPCs, dual-stack ALBs, AAAA DNS records, and IPv6 security group/WAF rules. Set false for IPv4-only VPCs. | bool | true | no |
private_subnets | Private subnets (CIDR blocks for VPC creation) | list(string) | ["10.0.4.0/24", "10.0.5.0/24", "10.0.6.0/24"] | no |
public_subnets | Public subnets (CIDR blocks for VPC creation) | list(string) | ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"] | no |
vpc_flow_log_cloudwatch_log_group_name | Optional explicit CloudWatch log group name for main VPC Flow Logs. When null, the VPC submodule keeps its generated random-suffix name. | string | null | no |
vpc_flow_log_iam_role_name | Optional explicit IAM role name for main VPC Flow Logs. When null, the VPC submodule keeps its generated random-suffix name. | string | null | no |
private_additional_transit_gateway_routes | Additional IPv4 routes to add to every private route table via a transit gateway. Only applies when this module creates the VPC. | list(object({ cidr_block = string transit_gateway_id = st... | [] | no |
existing_vpc_id | ID of an existing VPC to use instead of creating a new one. If provided, existing_private_subnet_ids and existing_public_subnet_ids must also be provided. | string | null | no |
existing_private_subnet_ids | List of existing private subnet IDs to use. Required if existing_vpc_id is provided. | list(string) | null | no |
existing_public_subnet_ids | List of existing public subnet IDs to use. Required if existing_vpc_id is provided. | list(string) | null | no |
cross_account_hosted_zone_id | Hosted zone ID for cross-account DNS validation (e.g., runlayer.com zone in management account). When set, caller must pass aws.route53 provider alias to module and configure cross-account access in that provider. | string | "" | no |
vpc_peering_connections | Map of VPC peering connections to accept and configure routes for. Only works when creating a new VPC (not with existing_vpc_id). Optional - defaults to no peering. SECURITY: peer_owner_id is now REQUIRED for all connections to prevent accepting connections from unknown sources. Validation: All VPC peering connections must have peer_owner_id specified. This is required for security validation to ensure you only accept connections from known and trusted AWS accounts.; VPC peering CIDR blocks must not overlap with the current VPC CIDR. Overlapping CIDR blocks cause routing conflicts and are not allowed. Please ensure peer_vpc_cidr does not overlap with vpc_cidr. | map(object({ peering_connection_id = string # VPC peering... | {} | no |
enable_vpc_endpoints | Enable VPC endpoints for AWS services to reduce NAT Gateway costs. Creates endpoints for S3 (gateway, FREE), ECR API, ECR Docker, and CloudWatch Logs (3 interface endpoints, ~$21.60/month). Only applies when creating a new VPC (not with existing_vpc_id). | bool | true | no |
ALB, DNS & WAF
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
alb_access_type | ALB access type (public allows internet access, private restricts to VPC) Validation: ALB access type must be ‘public’ or ‘private’. | string | "public" | no |
alb_allowed_cidrs | CIDR blocks allowed to access the ALB Validation: alb_allowed_cidrs must not contain 0.0.0.0/0 when alb_access_type is private. | list(string) | ["0.0.0.0/0"] | no |
alb_allowed_ipv6_cidrs | IPv6 CIDR blocks allowed to access the ALB Validation: alb_allowed_ipv6_cidrs must not contain ::/0 when enable_ipv6 is true and alb_access_type is private. | list(string) | ["::/0"] | no |
alb_additional_inbound_security_group_ids | Additional security group IDs allowed to reach the ALB | list(string) | [] | no |
alb_internal_allowed_cidrs | Additional CIDR blocks allowed to access the internal ALB in dual ALB mode. Effective internal ALB CIDRs are vpc_cidr + peered VPC CIDRs + this list. Validation: alb_internal_allowed_cidrs requires enable_dual_alb = true. | list(string) | [] | no |
alb_internal_allowed_ipv6_cidrs | Additional IPv6 CIDR blocks allowed to access the internal ALB in dual ALB mode. Validation: alb_internal_allowed_ipv6_cidrs requires enable_dual_alb = true. | list(string) | [] | no |
enable_dual_alb | Enable dual ALB setup (public + internal) for split-horizon DNS. When enabled, creates both a public ALB for internet traffic and an internal ALB for private network traffic. | bool | false | no |
enable_resolver_endpoint | Create a Route53 Resolver inbound endpoint so tailnet split-DNS can resolve the private hosted zone (domain_name -> internal ALB) over 4via6. Pairs with enable_dual_alb. | bool | false | no |
private_hosted_zone_id | Existing Route53 private hosted zone ID to use for internal DNS. If not provided and enable_dual_alb is true, a new private hosted zone will be created. | string | "" | no |
manage_service_vpc_private_hosted_zone_association | Whether to associate the service VPC with private_hosted_zone_id. Set false when another Terraform stack already manages that service VPC association. Validation: manage_service_vpc_private_hosted_zone_association = false is only meaningful when private_hosted_zone_id is set. | bool | true | no |
private_hosted_zone_vpc_id | VPC ID to associate with the private hosted zone. If empty, uses the service VPC (local.vpc_id). | string | "" | no |
private_hosted_zone_additional_vpc_ids | Additional VPC IDs to associate with the private hosted zone (e.g., for VPC peering scenarios). Same-account associations only; cross-account requires AWS RAM. | list(string) | [] | no |
ssl_certificate_arn | Existing SSL certificate ARN (optional - will look up wildcard certificate if not provided) | string | "" | no |
enable_acm_dns_validation | Create a new ACM certificate with DNS validation (false = look up existing wildcard cert) | bool | false | no |
create_dns_records | Create Route53 ALIAS record pointing domain to ALB (requires hosted_zone_name) | bool | false | no |
hosted_zone_name | Route53 hosted zone name for DNS records and/or ACM validation (required when create_dns_records or enable_acm_dns_validation is true) Validation: hosted_zone_name must be provided when enable_acm_dns_validation or create_dns_records is true. | string | "" | no |
alb_5xx_alarm_period | CloudWatch alarm period (seconds) for ALB 5XX errors | number | 300 | no |
alb_5xx_alarm_threshold | CloudWatch alarm threshold for ALB 5XX errors | number | 1 | no |
target_group_deregistration_delay | Deregistration delay in seconds for ALB target groups. Must exceed the longest in-flight MCP call (MCP_REQUEST_TIMEOUT = 104s) so the ALB doesn’t sever it mid-drain. Validation: target_group_deregistration_delay must be between 0 and 3600 seconds. | number | 120 | no |
blocked_path_patterns | URL path patterns blocked at the public ALB with a 404 (tailnet-only endpoints) | list(string) | [] | no |
waf_enable_ip_allowlisting | Enable WAF IP allowlisting to restrict ALB access to specific IPv4 and/or IPv6 CIDR blocks | bool | false | no |
waf_allowlist_ipv4_cidrs | List of user-supplied IPv4 CIDR blocks to allowlist in the WAF (e.g., [‘1.2.3.4/32’, ‘10.0.0.0/8’]). The effective IPv4 allowlist also includes the sandbox VPC NAT EIPs. Validation: When waf_enable_ip_allowlisting is true, provide at least one IPv4 CIDR block. | list(string) | [] | no |
waf_allowlist_ipv6_cidrs | List of user-supplied IPv6 CIDR blocks to allowlist in the WAF (e.g., [‘2001:db8::/48’]) Validation: When waf_enable_ip_allowlisting and enable_ipv6 are true, provide at least one IPv6 CIDR block. | list(string) | [] | no |
waf_webhook_allowlist_ipv4_cidrs | Additional IPv4 sources admitted only for POST agent webhook invocation; managed WAF rules and webhook authentication still apply. Validation: Nonempty lists require waf_enable_ip_allowlisting = true. Each entry must be a valid IPv4 CIDR with prefix length 1–32. Invalid configurations fail planning; empty lists remain valid when allowlisting is disabled. | list(string) | [] | no |
waf_webhook_allowlist_ipv6_cidrs | Additional IPv6 sources admitted only for POST agent webhook invocation; managed WAF rules and webhook authentication still apply. Validation: Nonempty lists require waf_enable_ip_allowlisting = true and enable_ipv6 = true. Each entry must be a valid IPv6 CIDR with prefix length 1–128. Invalid configurations fail planning; empty lists remain valid when either feature is disabled. | list(string) | [] | no |
waf_allowlist_exempt_uri_path_prefixes | URI path prefixes exempt from WAF IP allowlist blocking (e.g., [‘/.well-known/oauth-client/’]). Use for endpoints that must stay publicly reachable, such as CIMD documents fetched by upstream OAuth servers. AWS managed rules still apply. Empty list disables exemptions. Validation: Each waf_allowlist_exempt_uri_path_prefixes entry must start with ’/’ followed by at least one character. | list(string) | [] | no |
bad_inputs_count_uri_regex | URI path regex for requests whose AWSManagedRulesKnownBadInputsRuleSet body matches count instead of block. Header, path, query and method matches still block. Defaults to the client routes whose bodies carry user code or tool I/O: AI Watch hooks and scans, local MCP pre/post checks and tool sync, skill and plugin uploads, and skill file sync and scoring. Set empty to disable count-mode path matching. | string | "^/api/v1/(ai-watch/(scan|inventory|(skills|plugins)/submit(-batch)?)|hooks/(cursor|events(/batch)?|mcp-usage(/batch)?|tool/(pre|post))|local/[0-9a-fA-F-]{36}/(pre|post|capabilities)|skills/[0-9a-fA-F-]{36}/files(/[0-9a-fA-F-]{36})?|security/score/skill)$" | no |
bad_inputs_count_user_agent | User-Agent substring also required for bad_inputs_count_uri_regex count mode. Empty matches by URI path only, which covers every client of those routes (CLI, browser extension, agent sandbox, SDKs). | string | "" | no |
client_ip_header | HTTP header to extract client IP from for rate limiting. When set, uses this header’s value directly. When empty (default), uses x-forwarded-for with comma-split logic for proxy chains. For ECS with ALB, leave empty as ALB appends client IP to x-forwarded-for. | string | "" | no |
PrivateLink & tunnels
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
enable_privatelink | Enable an AWS PrivateLink endpoint service backed by an internal Network Load Balancer. Consumers connect with interface VPC endpoints using the privatelink_service_name output. | bool | false | no |
privatelink_allowed_principals | AWS principal ARNs allowed to create interface VPC endpoints to this PrivateLink service, usually customer account root ARNs. Validation: privatelink_allowed_principals entries must be IAM principal ARNs, such as arn:aws:iam::123456789012:root. | list(string) | [] | no |
privatelink_acceptance_required | Whether PrivateLink endpoint connection requests require manual acceptance by the service owner. In AgentCore VPC mode the module consumes its own service and accepts that connection itself, and the accepter can fire while the endpoint is still provisioning and then wait out its 20-minute timeout; set false when this account is the only consumer. Set it before the first VPC-mode apply: turning it off later destroys the accepter, which rejects the live connection. | bool | true | no |
privatelink_subnet_ids | Optional subnet IDs for the internal PrivateLink NLB. Defaults to the module private subnets. Validation: privatelink_subnet_ids must be null or contain at least one subnet ID. | list(string) | null | no |
privatelink_additional_supported_regions | Additional Regions from which service consumers can create cross-Region interface VPC endpoints to this PrivateLink service. The service host region is always included automatically from region. | set(string) | [] | no |
private_connection_types | Enabled private-connection types (“aws_privatelink”, “tunnel”). Empty disables private connections; delete all connections before removing a type. Validation: private_connection_types entries must be “aws_privatelink” or “tunnel”.; The “tunnel” private-connection type requires runlayer_deploy_use_shared_sg = false.; The tunnel type requires enable_vpc_endpoints = true when the module manages the VPC. | list(string) | [] | no |
tunnel_router_listener_priority | HTTPS listener-rule priority for the public /tunnel route. Validation: tunnel_router_listener_priority must be an unused integer between the /mcp alias and frontend catch-all priorities, and must differ from llm_gateway_listener_priority when the LLM gateway is enabled. | number | 13 | no |
tunnel_aws_endpoint_security_group_id | Existing-VPC security group dedicated to the ECR API, ECR DKR, CloudWatch Logs, and Secrets Manager interface endpoint ENIs used by tunnel tasks. Required when tunnel connections use an existing VPC; its endpoint associations must exist before this module evaluates them, and it must not be attached to any other ENI. Validation: Existing-VPC tunnel connections require tunnel_aws_endpoint_security_group_id. | string | null | no |
tunnel_trace_sample_ratio | Parent-based trace ID sampling ratio for tunnel-router and tunnel-endpoint OTEL traces. Metrics are not sampled. Validation: tunnel_trace_sample_ratio must be between 0 and 1. | number | 0.1 | no |
tunnel_router_repository | Untagged same-region private ECR repository for the shared tunnel-router image. Null defaults to the regional Runlayer mirror. Cross-account repositories must allow the ECS task execution role to pull. Validation: tunnel_router_repository must be an untagged private ECR repository in var.region, for example 123456789012.dkr.ecr.us-east-1.amazonaws.com/runlayer/tunnel-router. | string | null | no |
tunnel_endpoint_repository | Untagged same-region private ECR repository for per-connection tunnel endpoint images. Null defaults to the regional Runlayer mirror. Cross-account repositories must allow the ECS task execution role to pull. Validation: tunnel_endpoint_repository must be an untagged private ECR repository in var.region, for example 123456789012.dkr.ecr.us-east-1.amazonaws.com/runlayer/tunnel-endpoint. | string | null | no |
tunnel_connector_repository | Untagged Docker/OCI repository for the customer-run tunnel connector image. Defaults to Runlayer’s anonymously pullable public ECR repository. The customer runtime must be able to reach and authenticate to an override. Validation: tunnel_connector_repository must be an untagged Docker/OCI repository without a scheme or digest; registry host:port is supported. | string | "public.ecr.aws/d5v9c9x6/runlayer/tunnel-connector" | no |
tunnel_router_config | Sizing and per-task admission limits for the shared tunnel-router service. Validation: tunnel_router_config min_capacity and max_capacity must be positive integers with min_capacity no greater than max_capacity.; Tunnel router authorization, handshake, active-session, and WAF limits must be positive integers.; tunnel_router_config.max_concurrent_handshakes must not exceed max_active_sessions. | object({ cpu = number memory = number container_port = nu... | { cpu = 256 memory = 512 container_port = 8080 min_capacity = 2 max_capacity ... | no |
Database
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
database_name | Database name | string | "anysource" | no |
database_username | Database master username | string | "postgres" | no |
snapshot_identifier | Optional DB cluster snapshot name or ARN used to restore Aurora at create time only (ignored after). Master username must match the snapshot; password is set from app secrets PLATFORM_DB_PASSWORD. | string | null | no |
database_config | Aurora PostgreSQL Serverless v2 settings (all optional). The cluster is always db.serverless - size it with min_capacity/max_capacity in ACUs, not a provisioned instance class. enable_ops_reader adds another Serverless v2 reader. Use external_database to skip Aurora. | object({ engine_version = optional(string, "16") min_capa... | {} | no |
external_database | External PostgreSQL connection details. When set, Aurora RDS is not created. Set external_database_password separately. | object({ host = string reader_host = optional(string) ops... | null | no |
external_database_password | Password for external_database. Required when external_database is set. (sensitive) | string | null | no |
migration_lock_timeout_ms | Optional override for the backend migration task’s PostgreSQL lock_timeout, in milliseconds Validation: migration_lock_timeout_ms must be null or >= 0. | number | 5000 | no |
migration_statement_timeout_ms | Optional override for the backend migration task’s PostgreSQL statement_timeout, in milliseconds Validation: migration_statement_timeout_ms must be null or >= 0. | number | null | no |
migration_retry_count | Optional override for the backend migration task’s migration retry count Validation: migration_retry_count must be null or >= 0. | number | 180 | no |
backend_migration_aws_role | Optional IAM role configuration to assume before starting the backend migration ECS task. Validation: backend_migration_aws_role.arn must be non-empty when backend_migration_aws_role is set. | object({ arn = string external_id = optional(string) sess... | null | no |
backend_migration_wait_timeout_seconds | Maximum time to wait for the backend migration ECS task to stop. On timeout the task is stopped and the apply fails. Validation: backend_migration_wait_timeout_seconds must be a positive whole number. | number | 1800 | no |
enable_db_health_alarms | Enable Postgres health alarms (invalid indexes, auditlog partition runway/leaks, pg_partman maintenance failures) on the Runlayer/DatabaseHealth metrics emitted by the worker. Effective only when enable_monitoring and critical_alerts_enabled are set. | bool | true | no |
rds_database_connections_threshold | Threshold for RDS DatabaseConnections alarm. Default is ~80% of Aurora PostgreSQL Serverless v2 max_connections for the default 16 ACU. | number | 2700 | no |
rds_writer_cpu_utilization_threshold | Aurora WRITER CPUUtilization (%) above which the writer-cpu alarm fires after 3 consecutive 5-minute periods. Evaluated with Role = WRITER rather than the cluster average, which halves a pinned writer against an idle reader. Validation: rds_writer_cpu_utilization_threshold must be a percentage in (0, 100]. | number | 85 | no |
rds_reader_cpu_utilization_threshold | Aurora reader CPUUtilization (%) above which an instance’s reader-cpu alarm fires after 3 consecutive 5-minute periods. One metric-math alarm per cluster instance (DBInstanceIdentifier), gated on AuroraReplicaLag so it evaluates only while that instance is a reader and follows role swaps on failover; the Role = READER series averages the replicas and would mask one saturated reader next to an idle ops reader. Looser than the writer threshold: readers only serve replica-routed and ops/analytics reads. Validation: rds_reader_cpu_utilization_threshold must be a percentage in (0, 100]. | number | 90 | no |
rds_writer_acu_utilization_threshold | Aurora Serverless v2 WRITER ACUUtilization (%) at or above which the writer-acu alarm fires after 6 consecutive 5-minute periods. 100% means the writer is pinned at database_config.max_capacity and cannot scale further. The alarm is only created when database_config.min_capacity < max_capacity: a fixed-size cluster reads 100% permanently. Validation: rds_writer_acu_utilization_threshold must be a percentage in (0, 100]. | number | 90 | no |
db_idle_in_txn_max_duration_threshold | Alarm threshold (seconds) for longest idle-in-transaction session | number | 10 | no |
db_idle_in_txn_count_threshold | Alarm threshold for concurrent idle-in-transaction sessions | number | 10 | no |
rds_alarm_config | Map of RDS CloudWatch alarm configs for each metric. Each object must include period, threshold, and unit. Validation: All RDS alarm periods and thresholds must be positive numbers. | map(object({ period = number threshold = number unit = st... | { DiskQueueDepth = { period = 300 threshold = 5 unit = "Count" } WriteIOPS = {... | no |
prestart_container_cpu | CPU units for the backend migration task | number | 512 | no |
prestart_container_memory | Memory (MB) for the backend migration task | number | 1024 | no |
Redis / ElastiCache
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
external_redis | External Redis/Valkey connection details. When set, ElastiCache is not created. Set external_redis_password separately when auth is required. | object({ host = string port = optional(number, 6379) tls_... | null | no |
external_redis_password | Password/auth token for external_redis. Omit or leave null when the external Redis server has no auth. (sensitive) | string | null | no |
redis_curr_connections_threshold | Raw Redis CurrConnections backstop alarm threshold (per node, 3 of 3 five-minute periods). Normal fleet growth is covered by redis_connections_per_backend_task_threshold; this only catches a connection storm, so keep it well above max backend tasks × ~500 connections per task and below the node’s maxclients (65,000). | number | 20000 | no |
redis_connections_per_backend_task_threshold | Redis CurrConnections per running backend task (per node) above which the connection alarm pages, 3 of 3 five-minute periods. A backend task holds roughly 340-500 connections; worker services share the Redis URL but hold far fewer, so the default leaves headroom for them. Backend scale-out does not move this ratio; a sustained breach is a connection leak. Needs Container Insights (the ratio alarm is not created without it). | number | 750 | no |
critical_redis_curr_connections_threshold | Raw CurrConnections alarm threshold for the critical Redis cluster (per node, 3 of 3 five-minute periods). The backend opens only its shared client against this cluster, so the per-task threshold does not apply. Leave null to inherit: a redis_curr_connections_threshold you set yourself applies to the critical alarm too, as before; otherwise the critical alarm uses 2000. | number | null | no |
redis_evictions_threshold | Threshold for Redis Evictions alarm (sum over period) | number | 1 | no |
redis_burst_evictions_threshold | Eviction sum in a single 5-minute period that pages immediately (acute storm detection). Lower it if your steady-state eviction noise is well below the default. | number | 5000 | no |
redis_sustained_evictions_threshold | Hourly eviction sum that must be exceeded for 3 consecutive hours to trigger the sustained-evictions alarm. The default is set low enough to catch a slow, steady eviction bleed rather than only acute storms. | number | 500 | no |
redis_node_type | ElastiCache Redis node type | string | "cache.t3.medium" | no |
redis_num_cache_clusters | Number of Redis cache nodes in the replication group Validation: redis_num_cache_clusters must be 1 or greater. | number | 1 | no |
redis_automatic_failover_enabled | Enable automatic failover for Redis (requires 2+ nodes) | bool | false | no |
redis_multi_az_enabled | Enable multi-AZ for Redis (requires 2+ nodes and failover) | bool | false | no |
redis_snapshot_retention_limit | Days to retain ElastiCache Redis automatic backups. 0 disables. Null (default) resolves to 1 for production (remediates Security Hub ElastiCache.1) and 0 for staging/development. Validation: redis_snapshot_retention_limit must be null or between 0 and 35. | number | null | no |
enable_critical_redis | Create a dedicated ElastiCache replication group with maxmemory-policy=noeviction for correctness-critical ephemeral state, injected into ECS tasks as REDIS_CRITICAL_URL. Requires the managed ElastiCache Redis (not supported with external_redis) and wait_for_ecs_steady_state = true (keep it true for the group’s whole lifetime, including the apply that disables this flag). Validation: enable_critical_redis requires the managed ElastiCache Redis; it is not supported together with external_redis.; enable_critical_redis requires wait_for_ecs_steady_state = true: the disabling apply then observes the service drain instead of returning at API-accept. The group destroy is NOT graph-ordered after that drain — old tasks may fail critical-Redis operations (loudly, degrade-only) until replaced — so keep the wait enabled for the group’s whole lifetime, including the apply that disables this flag, and treat disables as a maintenance action.; enable_critical_redis: replication_group_id "${var.project}-${var.environment}-redis-critical" is ${length("${var.project}-${var.environment}-redis-critical")} characters; AWS caps replication group ids at 40. Shorten project and/or environment. | bool | false | no |
critical_redis_node_type | ElastiCache node type for the critical Redis replication group | string | "cache.t4g.small" | no |
critical_redis_num_cache_clusters | Number of cache nodes in the critical Redis replication group Validation: critical_redis_num_cache_clusters must be 1 or greater. | number | 1 | no |
critical_redis_multi_az_enabled | Enable multi-AZ for the critical Redis (requires 2+ nodes; failover is automatic with 2+ nodes) | bool | false | no |
sessions_write_payloads_to_redis | Hot-path Redis payload writes (ag_events bodies, ag_turns quad history). Flip to false ONLY after sessions_read_from_postgres = true in this env - the backend has an interlock that keeps writing until the durable path is complete and read from. Real-time keys are never gated. The flag itself is instantly revertible, but events from any off-window exist only in the durable store: rolling sessions_read_from_postgres back after an off-window renders those sessions empty from Redis (up to the 7d TTL horizon). | bool | true | no |
ECS services & scaling
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
workers | Number of workers for the backend | number | null # Set to null to use the number of CPUs on the node | no |
additional_backend_env_vars | Additional non-sensitive backend environment variables. Values override module-defined backend environment variables with the same name, except reserved module-managed values. Validation: module-managed deploy-encryption, analytics, and trusted-CA variables cannot be overridden. | map(string) | {} | no |
additional_backend_only_env_vars | Additional non-sensitive environment variables for the backend service task only — they do not reach the worker, the consumers, or the backend migration task. Use for settings whose runtime constraints differ per service (e.g. ECS_TASK_PROTECTION_ENABLED). Validation: keys already present in the shared backend environment (module-built or set in additional_backend_env_vars) are rejected, as are the module-managed deploy-encryption, analytics, and trusted-CA variables. | map(string) | {} | no |
trusted_ca_pem | Optional PEM bundle containing one or more additional trusted CA certificates. Combined with the image public roots for backend, shared worker, and MCP execution worker HTTPS clients. Private keys are rejected. | string | null | no |
backend_desired_count | Override the backend service’s initial desired_count without replacing the full services_configurations map. Only seeds a backend service that has never been created; Application Auto Scaling owns the live count afterwards. Prefer backend_min_capacity for a capacity floor. | number | null | no |
backend_min_capacity | Override backend service min_capacity without replacing the full services_configurations map | number | null | no |
backend_ephemeral_storage_size_gib | Ephemeral storage size in GiB for the backend ECS task. Validation: backend_ephemeral_storage_size_gib must be between 21 and 200 GiB for ECS Fargate. | number | 40 | no |
services_configurations | Validation: services_configurations must reserve backend.priority + 1 for the /mcp alias listener rule, before the frontend catch-all rule. | map(object({ name = string path_pattern = list(string) he... | { "backend" = { name = "backend" path_pattern = ["/api/*", "/docs*", "/redoc*... | no |
frontend_sentry_dsn | Public frontend Sentry DSN. Overrides the optional frontend_sentry_dsn key of the WorkOS Vault relay secret. Empty defers to that Vault key; frontend Sentry stays disabled when neither is set. Validation: frontend_sentry_dsn must be empty or a public HTTP(S) Sentry DSN without a password. | string | "" | no |
frontend_sentry_traces_sample_rate | Frontend Sentry trace sample rate passed unchanged to the browser runtime. Runtime configuration owns parsing and disable behavior. | string | "0.1" | no |
backend_stop_timeout_seconds | Grace period (seconds) between SIGTERM and SIGKILL for backend containers. Must cover uvicorn’s graceful shutdown of the longest in-flight request (timeout_graceful_shutdown = 105s, just above MCP_REQUEST_TIMEOUT = 104s); 120s is the ECS/Fargate maximum. Validation: backend_stop_timeout_seconds must be between 2 and 120 seconds for ECS/Fargate. | number | 120 | no |
health_check_grace_period_seconds | Grace period in seconds before health checks start | number | 120 | no |
wait_for_ecs_steady_state | Wait for ECS services to reach steady state before Terraform apply completes. | bool | false | no |
worker_config | Worker configuration for background job processing | object({ cpu = optional(number, 1024) memory = optional(n... | {} | no |
worker_exclude_entrypoints | Comma-separated job names to exclude from the main worker (empty = register all cron jobs on main worker). execute_tool_call is always excluded regardless — the dedicated mcp-exec-worker service owns that lane. | string | "" | no |
mcp_exec_worker_config | Dedicated worker service for the execute_tool_call pgqueuer lane (worker-tier MCP tool calls). Deployable-but-idle by default: the lane receives no traffic until the worker-tier flag flips. Validation: mcp_exec_worker_config: min_capacity and desired_count must be >= 1 (scale-from-zero is unsupported; use infra_parked to park), and max_capacity must be >= min_capacity. | object({ cpu = optional(number, 1024) memory = optional(n... | {} | no |
mcp_exec_worker_ephemeral_storage_size_gib | Ephemeral storage size in GiB for the mcp-exec-worker ECS task. Default matches the backend’s: both host the same 10GiB-budget stdio npm cache, which ENOSPC’d the backend’s original 20GiB default. Validation: mcp_exec_worker_ephemeral_storage_size_gib must be between 21 and 200 GiB for ECS Fargate. | number | 40 | no |
ecs_service_wait_timeout | Timeout for ECS service create/update operations when waiting for steady state. The backend’s task scale-in protection hard cap (ECS_TASK_PROTECTION_HARD_CAP_SECONDS) assumes the 120m default; if you lower this below ~100 minutes, lower ECS_TASK_PROTECTION_HARD_CAP_SECONDS to stay under it so a protected task can never outlast the wait. | string | "120m" | no |
enable_ecs_exec | Enable ECS Exec for interactive shell access to backend and worker containers. Use only for development and testing purposes. | bool | false | no |
enable_read_only_root_filesystem | Enable read-only root filesystem for ECS containers. | bool | false | no |
ec2_metadata_http_tokens | EC2 instance metadata token requirement for module-managed launch templates. Use required to enforce IMDSv2. Validation: ec2_metadata_http_tokens must be either required or optional. | string | "required" | no |
backend_cors_origins | Optional list of backend CORS origins; leave empty to let the backend default to APP_URL. Validation: backend_cors_origins entries must start with http:// or https:// | list(string) | [] | no |
Monitoring & alerts
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
container_insights | ECS cluster Container Insights setting. Null (default) resolves to “enabled” for production and “disabled” for all other environments. Validation: container_insights must be null or one of: enhanced, enabled, disabled. | string | null | no |
enable_monitoring | Enable CloudWatch monitoring and alarms | bool | false | no |
critical_alerts_enabled | Enable mission-critical CloudWatch alarms and wire them to the critical alerts SNS topic | bool | true | no |
enable_agent_run_alarms | Enable agent-run health alarms (stalled in-flight runs, run failure rate) on the Runlayer/AgentRuns metrics emitted by the worker. Effective only when enable_monitoring and critical_alerts_enabled are set. | bool | true | no |
enable_worker_queue_alarms | Enable the mcp-exec-worker queue dead-man alarm on the Runlayer/WorkerQueue metrics emitted by the shared worker (missing data breaches: a dark metric freezes the lane’s autoscaling). Effective only when enable_monitoring and critical_alerts_enabled are set. | bool | true | no |
agent_run_stall_threshold_minutes | Minutes a run may sit in running with no new run event before it counts as stalled. Documentation for the alarm text only — the worker reads its own AGENT_RUN_STALL_THRESHOLD_MINUTES setting, so change both together. Validation: agent_run_stall_threshold_minutes must be positive. | number | 60 | no |
agent_run_stalled_count_threshold | Number of concurrently stalled agent runs tolerated before the alarm fires. 0 means any stalled run pages. Validation: agent_run_stalled_count_threshold must not be negative. | number | 0 | no |
agent_run_failure_rate_threshold_percent | Percentage of agent runs finishing in the rolling window that may fail before paging. Usage-limit rejections and user cancellations are excluded from the rate by the emitter. Validation: agent_run_failure_rate_threshold_percent must be between 0 and 100. | number | 5 | no |
agent_run_failure_rate_min_volume | Minimum runs finishing in the rolling window before the failure-rate alarm evaluates. Below this the rate is forced to 0, so a single failure at low volume does not page. A deployment whose hourly volume sits under this value can never fire the alarm at all — lower it there (toward 1, where any single failure pages) rather than raising it. Validation: agent_run_failure_rate_min_volume must be at least 1. | number | 20 | no |
critical_alerts_create_sqs_subscription | Create an SQS queue subscribed to the critical alerts SNS topic (useful for Slack/ops ingestion pipelines). The queue is encrypted with the critical-alerts CMK: consumers need kms:Decrypt on critical_alerts_kms_key_arn (IAM-grantable; the key policy delegates to account IAM). | bool | true | no |
critical_alerts_extra_sns_topic_arns | Extra SNS topic ARNs to fan out critical alarms to (e.g. an internal Slack relay). Must be in the same region as this module. CloudWatch allows five actions per alarm, and the ToolGuard alarms already carry their own topic plus the critical-alerts topic when that route exists, so with ToolGuard alarms enabled at most 3 extra topics fit (4 when enable_monitoring or critical_alerts_enabled is off). Validation: Too many critical_alerts_extra_sns_topic_arns for the ToolGuard alarms: CloudWatch allows five actions per alarm, and those alarms already carry their own topic plus the critical-alerts topic when enable_monitoring and critical_alerts_enabled are on, so at most 3 extra topics fit (4 with that route off). | list(string) | [] | no |
enable_runlayer_topic_cpu_alarms | Enable CloudWatch alarms for topic CPU ECS service | bool | true | no |
enable_runlayer_deploy_healthcheck | Run the MCP telemetry proxy as a health-check sidecar for Runlayer Deploy ECS tasks without routing MCP traffic through it. | bool | false | no |
OpenTelemetry
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
otel_exporter_otlp_metrics_endpoint | Optional OTEL_EXPORTER_OTLP_METRICS_ENDPOINT for the backend ECS task. Set this to enable backend OTEL metric export. | string | null | no |
otel_exporter_otlp_metrics_protocol | Optional OTEL_EXPORTER_OTLP_METRICS_PROTOCOL for the backend ECS task. Only used when otel_exporter_otlp_metrics_endpoint is set. | string | null | no |
otel_service_name | Optional OTEL_SERVICE_NAME for the backend ECS task. Only used when otel_exporter_otlp_metrics_endpoint is set. | string | null | no |
otel_collector_config | Optional otel-collector sidecar for the backend and worker ECS tasks. When enabled, adds a second container running the OpenTelemetry Collector that receives telemetry via localhost:4317. export_endpoint is the OTLP gRPC endpoint of a customer-OWNED collector; leave it null when none exists and the customer export pipeline is omitted entirely (loopback values are rejected at plan time — they point at the sidecar itself). Runlayer export is configured separately via runlayer_obs_otel_*. | object({ enabled = optional(bool, false) image = optional... | {} | no |
flow_metrics_collection_enabled | Backend flow-metric generation (FLOW_TRACE_COLLECTION_ENABLED). Defaults (null) to runlayer_obs_otel_enabled. Set explicitly to decouple flow collection from export transport. | bool | null | no |
flow_trace_adaptive_sampling_enabled | Adaptive sampling for hot-path flow traces (FLOW_TRACE_ADAPTIVE_SAMPLING_ENABLED). A cost lever, deliberately independent of the contractual collection permission (runlayer_obs_otel_enabled): it sheds local step-recording work even when remote export is off. | bool | false | no |
LLM gateway & Bedrock
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
enable_llm_gateway | Deploy the Runlayer LLM gateway ECS service behind the application ALB. | bool | false | no |
llm_gateway_image_uri | Optional LLM gateway image URI. Defaults to the Runlayer customer-distribution image for app_version. Validation: llm_gateway_image_uri must include an explicit tag or digest. | string | null | no |
llm_gateway_runlayer_api_key | Optional org-bound Runlayer user API key (must hold MANAGE_ORG_SETTINGS) for the LLM gateway control plane. Required only on multi-organization deployments; leave empty on single-org deployments and the module generates a machine sync secret shared with the backend instead. (sensitive) | string | (sensitive — omitted) | no |
llm_gateway_listener_priority | HTTPS listener-rule priority for /openai and /anthropic gateway routes. Validation: llm_gateway_listener_priority must be unused and between the /mcp alias and frontend catch-all priorities. | number | 12 | no |
anthropic_api_key | Anthropic API key for Claude AI models (sensitive) | string | (sensitive — omitted) | no |
mcp_tunnel_base_url | Optional Anthropic MCP Gateway tunnel base URL (https://…). When set, server MCP OAuth PRM/AS metadata uses this host; browser auth stays on APP_URL. Single-tenant deployments only. | string | null | no |
bedrock_region | AWS region for Bedrock API calls. Defaults to the deployment region when empty. | string | "" | no |
bedrock_auto_model_access | Let the backend grant this AWS account Bedrock model access on its own (submits Anthropic’s use case details form and accepts the model agreement). Disable to keep model access a manual step in the AWS console. | bool | true | no |
bedrock_model_access_industry | Industry named on Anthropic’s Bedrock use case details form, alongside bedrock_model_access_company_name. Defaults to the operator’s. Override it if you run this install yourself and are in another sector — the form is submit-once and permanent. | string | "Software as a Service" | no |
bedrock_model_access_company_website | Company website named on Anthropic’s Bedrock use case details form. Not the deployment URL — that is often an internal hostname. Override alongside bedrock_model_access_company_name if you run this install yourself. | string | "https://runlayer.com" | no |
bedrock_model_access_intended_users | Intended user count on Anthropic’s Bedrock use case details form. A deployment-level estimate, not a live count — the form is submit-once, so raise it if this deployment serves a larger audience. | number | 2 | no |
bedrock_model_access_company_name | Company named on Anthropic’s Bedrock use case details form, which the backend submits automatically so the Runlayer Assistant works on a fresh AWS account. Defaults to the operator of the software making the calls. Override for a self-managed install run by the customer themselves. To skip submission entirely, set bedrock_auto_model_access = false — an empty name here is discarded and falls back to the default. Never derived from tenant data: the form is submit-once and permanent. | string | "Runlayer" | no |
enable_bedrock_anthropic_models | Enable Bedrock Anthropic models for regular agent runs. Default false so single-tenant/customer deployments do not expose them unless explicitly opted in. Note: the Runlayer Assistant always routes through Bedrock regardless of this flag. | bool | false | no |
platform_llm_trial_default_budget_usd | Default trial inference budget in USD stamped on newly created organizations. Empty leaves the capability opt-in for generic module consumers; Runlayer-managed tenant templates set it explicitly. | string | "" | no |
braintrust_api_key | Braintrust API key for agent tracing (optional) (sensitive) | string | (sensitive — omitted) | no |
braintrust_project | Braintrust project name for agent tracing | string | "Runlayer Agents" | no |
Agents, ToolGuard & topic CPU
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
enable_runlayer_tool_guard | Enable Runlayer ToolGuard Flask server deployment | bool | false | no |
runlayer_tool_guard_desired_count | Initial capacity for Runlayer ToolGuard ASG (1–5); capacity provider manages EC2 instance scaling Validation: Runlayer ToolGuard desired count must be between 1 and 5 | number | 2 | no |
runlayer_tool_guard_max_count | Maximum horizontally autoscaled Runlayer ToolGuard task/GPU instance count Validation: Runlayer ToolGuard max count must be between 2 and 10 | number | 6 | no |
runlayer_tool_guard_gpu_resource_value | GPU resource value requested by the Runlayer ToolGuard ECS task. Use ALL when ECS rejects an integer request for a fractional GPU instance. Validation: runlayer_tool_guard_gpu_resource_value must be either 1 or ALL. | string | "ALL" | no |
runlayer_tool_guard_dynamic_batch_max | Max items per coalesced GPU forward in the ToolGuard dynamic batcher. 16 is burst-gate validated on g6f.large and is the default. Set to 1 to disable batching and restore inline prediction. Validation: runlayer_tool_guard_dynamic_batch_max must be between 1 and 64. | number | 16 | no |
runlayer_tool_guard_health_check_grace_period_seconds | ECS health_check_grace_period_seconds for the ToolGuard service. Unset (null) uses the module’s GPU host boot window (900s: GRID driver install + model load), shared with the ASG health check grace period and the capacity provider instance warmup. Set to 2147483647 (int32 max) as a scoped stopgap where ECS is looping on task replacement because the current image’s worker-recycle drain flips /health to 503 and every task owns a whole GPU; restore the default once the worker-rotation image (no /health flip) is pinned. Validation: runlayer_tool_guard_health_check_grace_period_seconds must be between 0 and 2147483647. | number | null | no |
runlayer_tool_guard_timeout | Deprecated: enforcement scans take their budget from the workspace scan latency budget (Security Scanners -> Scan budgets, 50 ms-300 s, fail-open or fail-closed) once it is set; this value is adopted into that setting whenever it is unset. Timeout in whole seconds for every other ToolGuard request the backend makes (tool-list scans, analyze, models), which read it directly and fail open on timeout. Validation: Runlayer ToolGuard timeout must be between 5 and 300 seconds | number | 5 | no |
runlayer_tool_guard_log_retention_days | CloudWatch log retention in days for Runlayer ToolGuard. Longer retention recommended for security auditing and compliance. Validation: Log retention must be a valid CloudWatch retention period (e.g., 7, 30, 60, 90, 180, 365 days) | number | 30 | no |
enable_runlayer_tool_guard_alarms | Enable CloudWatch alarms for Runlayer ToolGuard service health and performance monitoring | bool | true | no |
runlayer_tool_guard_scaling_profile | How aggressively ToolGuard adds GPU capacity under load. conservative (default): CPU target 50%, capacity provider target 100 (no spare EC2 headroom), scale-out step = runlayer_tool_guard_desired_count instances, 60s scale-out cooldown. balanced: CPU 35%, capacity target 80 (~20% spare instance headroom), two instances per step, 30s cooldown. aggressive: CPU 25%, capacity target 70 (~30% headroom), steps up to runlayer_tool_guard_max_count at once, 30s cooldown, plus a second target-tracking policy on fleet-average GPU utilization (target 60%; hosts publish GPU metrics every 60s, no new CloudWatch series). Scale-in cooldown is 900s for every profile. Higher profiles keep more idle g6f.large hours warm; new GPU hosts still take 10-15 min to install drivers, so headroom is what actually shortens time-to-serve. Validation: runlayer_tool_guard_scaling_profile must be one of: conservative, balanced, aggressive. | string | "conservative" | no |
runlayer_tool_guard_predictive_scaling_mode | ECS predictive scaling for ToolGuard. Forecasts the next 48h of task demand from up to 14 days of ToolGuard predict-request history and launches tasks runlayer_tool_guard_predictive_scheduling_buffer_seconds ahead of each forecast hour, so GPU hosts are booting before the load arrives instead of after. forecast_only records forecasts without acting (the AWS-recommended evaluation mode; the first forecast needs 24h of history). forecast_and_scale acts on them. disabled creates no policy. Unset (null) derives from runlayer_tool_guard_scaling_profile: conservative -> forecast_only, balanced/aggressive -> forecast_and_scale. Automatically disabled in regions where ECS predictive scaling is unavailable (a plan warning names the region). Pair forecast_and_scale with container_insights = enabled: without Container Insights the requests-per-task metric divides by ASG in-service hosts, which include the capacity provider’s spare hosts (~20% on balanced, ~30% on aggressive), so it reads low and the forecast under-provisions. Predictive scaling only scales out; the CPU target-tracking policy still handles scale-in and unexpected bursts. Validation: runlayer_tool_guard_predictive_scaling_mode must be one of: disabled, forecast_only, forecast_and_scale. | string | null | no |
runlayer_tool_guard_predictive_scheduling_buffer_seconds | Seconds before each forecast hour at which ToolGuard predictive scaling launches the forecast task count. The default covers a g6f.large host boot plus GRID driver install (the service’s 900s health_check_grace_period) and task start, so forecast capacity is serving at the top of the hour. Validation: runlayer_tool_guard_predictive_scheduling_buffer_seconds must be between 0 and 3600. | number | 1200 | no |
runlayer_tool_guard_predictive_load_floor_fraction | Weekday floor for the predictive load metric, as a fraction of the mean hourly ToolGuard predict-request count over the forecast window. Hours below the floor are raised to it before forecasting so days when scanning was disabled do not teach predictive scaling that the weekday is idle. 0 disables the floor. Validation: runlayer_tool_guard_predictive_load_floor_fraction must be between 0 and 1. | number | 0.5 | no |
runlayer_tool_guard_predictive_load_floor_weekdays_only | Apply the predictive load floor Monday-Friday (UTC) only; weekends forecast from actual traffic. false floors every day. | bool | true | no |
runlayer_tool_guard_predictive_target_requests_per_task_per_minute | Per-task predict-request rate, per minute, that ToolGuard predictive scaling sizes forecast capacity to. Application Auto Scaling evaluates throughput scaling metrics over one-minute intervals, so the target must be a per-minute figure. The default (50/min = 3000/h) keeps per-task load in the range where predict p95 stays near its floor; latency rises steeply past roughly twice this rate. Raise to trade latency for fewer hosts, lower for more headroom. Validation: runlayer_tool_guard_predictive_target_requests_per_task_per_minute must be between 2 and 2000. | number | 50 | no |
runlayer_tool_guard_predictive_target_requests_per_task_per_hour | Deprecated: use runlayer_tool_guard_predictive_target_requests_per_task_per_minute. When set, this per-hour value is divided by 60 and used as the per-minute target. Validation: runlayer_tool_guard_predictive_target_requests_per_task_per_hour must be between 120 and 120000 (2–2000 per minute). | number | null | no |
enable_runlayer_topic_cpu | Enable dedicated ECS EC2 service for CPU topic_cascaded inference (AgentGuard session topics) | bool | false | no |
runlayer_topic_cpu_desired_count | Initial capacity for topic CPU ASG (1–5); capacity provider manages EC2 instance scaling Validation: runlayer_topic_cpu_desired_count must be between 1 and 5 | number | 1 | no |
runlayer_topic_cpu_gunicorn_workers | Worker processes in the topic CPU slow-pool container. Default 4 is the recommended production sizing; lower values let batch scanning work occupy every slot and delay interactive scans. Each worker needs its own memory allocation, so raise runlayer_topic_cpu_memory alongside it. Validation: runlayer_topic_cpu_gunicorn_workers must be between 1 and 6; runlayer_topic_cpu_memory must be at least workers * 2048 MiB, or workers * 2560 MiB while enable_runlayer_topic_cpu and runlayer_skill_guard_enabled are both true (SkillGuard scans peak at ~2.2 GiB per worker). Raise runlayer_topic_cpu_memory, lower runlayer_topic_cpu_gunicorn_workers, or set runlayer_skill_guard_enabled = false. | number | 4 | no |
runlayer_topic_cpu_instance_type | EC2 instance type for topic CPU capacity provider (host must fit both containers: slow + fast hard limits + OS/agent overhead; default m7i.2xlarge = 32 GiB, registers ~31.4k MiB to ECS and fits slow 16384 + fast 8192 defaults) | string | "m7i.2xlarge" | no |
runlayer_topic_cpu_memory | Hard memory limit (MiB) for the topic CPU slow-pool container. Measured (2026-10-16, 7 days, per-container): ~1.7 GiB per worker resident, ~1.85 GiB average and ~2.2 GiB peak per worker with SkillGuard scanning on. Budget at least 2048 MiB per worker (2560 with runlayer_skill_guard_enabled) plus spike headroom; the 16384 default is sized for the default 4 workers with SkillGuard. Slow + fast limits must fit the instance type (m7i.2xlarge registers ~31.4k MiB to ECS): 16384 + 8192 = 24576. Validation: runlayer_topic_cpu_memory must be at least 2048 MiB | number | 16384 | no |
runlayer_topic_cpu_fast_gunicorn_workers | Worker processes in the topic CPU fast-pool container, which serves latency-sensitive scans. Agents can issue large bursts of concurrent scans, so the default 6 is sized for burst headroom; too few workers serialize a burst past the client timeout budget. Fast-pool workers are comparatively cheap memory-wise. Validation: runlayer_topic_cpu_fast_gunicorn_workers must be between 1 and 6; fast memory must be at least fast workers * 1024 MiB, which is a floor with no spike headroom — size above it. Raise runlayer_topic_cpu_fast_memory or lower runlayer_topic_cpu_fast_gunicorn_workers. | number | 6 | no |
runlayer_topic_cpu_fast_memory | Hard memory limit (MiB) for the topic CPU fast-pool container. Size to roughly 1 GiB per fast worker plus headroom; the 8192 default leaves ~2 GiB spare at the default 6 workers. Slow + fast limits must fit the instance type (m7i.2xlarge registers ~31.4k MiB to ECS): 16384 + 8192 = 24576. An OOM kill here restarts the whole task, so do not size this tight. Validation: runlayer_topic_cpu_fast_memory must be at least 2048 MiB | number | 8192 | no |
runlayer_topic_cpu_fast_cpu | DEPRECATED in favor of runlayer_topic_cpu_fast_cpu_share. Absolute CPU units (1024 = 1 vCPU) for the topic CPU fast-pool container; when set it overrides the share-derived value, so it silently stops scaling if runlayer_topic_cpu_instance_type changes. Kept so existing deployments that set it keep planning; leave null to derive from the instance type. Validation: runlayer_topic_cpu_fast_cpu must be between 256 and 2048 | number | null | no |
runlayer_topic_cpu_fast_cpu_share | Fraction of the instance’s CPU units (vcpus * 1024) reserved for the topic CPU fast-pool container; the slow pool gets the remainder. ECS cpu is a share (soft) on EC2, so this is the fast pool’s guaranteed floor under full contention. The default 0.25 keeps enough headroom for the fast pool to drain a burst of concurrent scans within its client timeout budget even while the slow pool is saturated. Shares summing to 100% of vcpus*1024 is placement-safe: ECS on EC2 registers the full capacity to the host. Validation: runlayer_topic_cpu_fast_cpu_share must be strictly between 0 and 1 | number | 0.25 | no |
runlayer_skill_guard_enabled | Master switch for the SkillGuard skill scanner (RUNLAYER_SKILL_GUARD_ENABLED). It shares the topic-cpu endpoint: every deployment with enable_runlayer_topic_cpu scores skills with SkillGuard unless this is false. Set false to keep a deployment on the legacy skill scorer, for example a topic-cpu shape sized below the defaults (see runlayer_topic_cpu_memory and runlayer_topic_cpu_gunicorn_workers). | bool | true | no |
runlayer_topic_cpu_timeout | Backend HTTP client timeout in seconds for RUNLAYER_TOPIC_CASCADED requests Validation: runlayer_topic_cpu_timeout must be between 5 and 300 | number | 30 | no |
enable_runlayer_rev | Run Rev, Runlayer’s self-hosted decision model, as its own GPU ECS service (runlayer-rev, g6f.large, NLB :8080, POST /predict/runlayer-rev) and set RUNLAYER_REV_ENDPOINT_URL / RUNLAYER_REV_TIMEOUT on the backend. Independent of ToolGuard and the topic CPU stack. | bool | false | no |
enable_runlayer_rev_alarms | Enable CloudWatch alarms for the Rev service (unhealthy targets; no healthy target for 20 minutes, which also catches a host that never registers), delivered to the Rev alerts topic and the critical-alerts topic when that route exists. | bool | true | no |
runlayer_rev_desired_count | Rev tasks (= g6f.large hosts; each task takes the host’s whole GPU). One slice serves ~8.6 req/s at 256 tokens. A deploy surges one extra host, which needs the 10-15 min GRID driver boot. Validation: 1..4. | number | 1 | no |
runlayer_rev_timeout | Backend HTTP client timeout in seconds per Rev decision request (RUNLAYER_REV_TIMEOUT). Ceiling for every Rev caller. A bounded request (1 chunk of 2400 chars x 8 rules) takes ~1.5 s on the g6f.large slice; anything longer means Rev is overloaded and the cold-path smart rules analyzer fails open. Not on the hot path. Validation: 1..60 (matches operator topicCpu.rev.timeoutSeconds). | number | 15 | no |
runlayer_topic_cpu_log_retention_days | CloudWatch log retention in days for topic CPU service Validation: Log retention must be a valid CloudWatch retention period | number | 30 | no |
enable_runlayer_agents | Enable RunLayer Agents feature and Agent Sandbox infrastructure. Set to true to show Agents management in the Admin section of the UI. | bool | false | no |
enable_runlayer_cimd | Deprecated no-op. CIMD is always enabled. | bool | true | no |
runlayer_agent_sandbox_image_uri | Docker image URI for the Agent Sandbox Runner. Defaults to the CustomerDistribution ECR image tagged with app_version. | string | null | no |
runlayer_agent_sandbox_image_repository_arn | ECR repository ARN for the Agent Sandbox Runner image. Defaults to the repository derived from runlayer_agent_sandbox_image_uri. | string | null | no |
runlayer_agent_sandbox_sentry_dsn | Separate Sentry DSN for Agent Sandbox error tracking. Empty disables Agent Sandbox Sentry reporting. (sensitive) | string | (sensitive — omitted) | no |
enable_runlayer_agentcore_runtime | Provision AgentCore runtime infrastructure for agent runs. | bool | null | no |
runlayer_agentcore_network_mode | AgentCore runtime network mode. PUBLIC keeps AgentCore public networking. VPC attaches AgentCore ENIs to a consumer VPC and reaches Runlayer through the PrivateLink endpoint service. Validation: runlayer_agentcore_network_mode must be PUBLIC or VPC.; runlayer_agentcore_network_mode = VPC requires enable_privatelink = true.; runlayer_agentcore_network_mode = VPC requires either runlayer_agentcore_consumer_vpc_id with non-empty runlayer_agentcore_consumer_private_subnet_ids, or a module-created consumer VPC: leave both consumer subnet lists unset to derive them, or set runlayer_agentcore_consumer_vpc_cidr_block with equally long, non-empty private and public subnet CIDR lists (one pair per consumer AZ). | string | "PUBLIC" | no |
runlayer_agentcore_consumer_vpc_id | Existing consumer VPC ID for AgentCore VPC networking. Required with runlayer_agentcore_consumer_private_subnet_ids when using an existing consumer VPC. Supplied private subnets must already have outbound HTTPS egress, such as through NAT. | string | null | no |
runlayer_agentcore_consumer_private_subnet_ids | Private subnet IDs in the existing AgentCore consumer VPC. Required with runlayer_agentcore_consumer_vpc_id when using an existing consumer VPC. These subnets must already route outbound HTTPS through NAT or equivalent egress (see Self-Hosted Egress Requirements). | list(string) | null | no |
runlayer_agentcore_consumer_vpc_cidr_block | CIDR block for the module-created AgentCore consumer VPC. Defaults to 10.112.0.0/20: the consumer VPC reaches Runlayer only through PrivateLink and is never peered, so its range may overlap any other VPC. | string | null | no |
runlayer_agentcore_consumer_private_subnets | Private subnet CIDR blocks for the module-created AgentCore consumer VPC, one per consumer AZ. Unset (with the public list also unset) derives one per selected AZ from the second half of the consumer VPC CIDR block (10.112.8.0/24 onward by default). | list(string) | null | no |
runlayer_agentcore_consumer_public_subnets | Public subnet CIDR blocks in the module-created AgentCore consumer VPC (NAT gateway, or the sandbox egress proxy tasks in proxy_only), one per consumer AZ. Unset (with the private list also unset) derives one per selected AZ from the first half of the consumer VPC CIDR block (10.112.0.0/24 onward by default). | list(string) | null | no |
sandbox_egress_mode | How AgentCore sandboxes reach the internet; each value is one rollout step in docs-internal/decision-records/2026-09-16-sandbox-egress-proxy.md. “direct” (default): the runtime security group allows HTTPS to 0.0.0.0/0 through NAT and no proxy exists. “proxy”: also deploy the sandbox egress proxy (identity-aware HTTP CONNECT forward proxy, ECS service behind an internal NLB in the consumer VPC) with the consumer VPC’s S3, ECR and CloudWatch Logs endpoints, and hand its URL to the backend as SANDBOX_EGRESS_PROXY_URL. Runs route through it once the per-organization sandbox-egress-proxy feature flag is on; the direct rule stays, so turning an organization’s flag off sends its next runs direct again without an apply. “proxy_only”: remove the direct rule, scope the endpoint policies and enable the consumer VPC DNS firewall. The proxy becomes the only internet path, so a proxy outage stops all sandbox internet traffic; the backend routes every run through it whatever the flag says and is told policies are enforced (SANDBOX_EGRESS_DIRECT_BYPASS_CLOSED). proxy and proxy_only require runlayer_agentcore_network_mode = VPC. Validation: sandbox_egress_mode must be direct, proxy or proxy_only.; sandbox_egress_mode proxy and proxy_only require runlayer_agentcore_network_mode = VPC; the proxy lives in the AgentCore consumer VPC and is reachable only from the runtime security group. | string | "direct" | no |
sandbox_egress_proxy_keep_nat_gateway | Keep the module-created AgentCore consumer VPC’s NAT gateway in sandbox_egress_mode = “proxy_only”. By default proxy_only drops it (nothing else in that VPC needs internet) and runs the proxy tasks in the public subnets with their own public IPs, which change whenever a task is replaced. Set true when third parties allowlist the sandboxes’ egress IP: the proxy then stays in the private subnets behind the NAT’s Elastic IP. | bool | false | no |
sandbox_egress_proxy_image_uri | Optional sandbox egress proxy image URI. Defaults to the Runlayer customer-distribution image for app_version. Validation: sandbox_egress_proxy_image_uri must include an explicit tag or digest. | string | null | no |
sandbox_egress_proxy_dns_servers | Upstream DNS resolvers (IPv4) the sandbox egress proxy uses for destination lookups. The consumer VPC’s DNS firewall (active once direct egress is off) answers NXDOMAIN for public names and applies to the proxy too, so it must not use the VPC resolver. Point at your own resolvers if public ones are not acceptable. Validation: sandbox_egress_proxy_dns_servers must be a non-empty list of IPv4 addresses. | list(string) | ["1.1.1.1", "8.8.8.8"] | no |
sandbox_egress_proxy_platform_allow_hosts | Additional platform host patterns the sandbox egress proxy allows unless the agent’s policy explicitly denies them. Empty by default; the Sentry ingest host from runlayer_agent_sandbox_sentry_dsn is added automatically. Package registries and models.dev follow the agent’s network policy. Same grammar as agent policies (host, *.host, optional :port). Validation: sandbox_egress_proxy_platform_allow_hosts entries must be lowercase host patterns: host, *.host, optionally :port. | list(string) | [] | no |
sandbox_egress_proxy_egress_ports | Destination TCP ports the sandbox egress proxy security group may open to the internet. 443 is always included (the task itself needs it for ECR, Secrets Manager and CloudWatch Logs); list additional ports that agent egress policies may allow as host:port. Without an explicit port a policy allows 443 only. Validation: sandbox_egress_proxy_egress_ports must be a non-empty list of TCP ports between 1 and 65535. | list(number) | [443] | no |
runlayer_agentcore_max_lifetime_seconds | Maximum lifetime in seconds for AgentCore runtime sessions. Validation: runlayer_agentcore_max_lifetime_seconds must be between 60 and 28800 seconds. | number | 28800 | no |
runlayer_agentcore_idle_timeout_seconds | Idle timeout in seconds for AgentCore runtime sessions. Bounds how long a warm conversation microVM survives between runs; in-run waits keep the session busy and are unaffected. Validation: runlayer_agentcore_idle_timeout_seconds must be between 60 and 28800 seconds.; runlayer_agentcore_idle_timeout_seconds must not exceed runlayer_agentcore_max_lifetime_seconds (AgentCore rejects idle timeouts above the session max lifetime). | number | 900 | no |
enable_toolguard_feedback | Enable S3 storage for customer-submitted ToolGuard false-positive / false-negative feedback. When enabled, creates an S3 bucket and grants backend write access. | bool | true | no |
Runlayer Deploy
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
runlayer_deploy_telemetry_proxy_image_uri | Full image URI for the Runlayer Deploy MCP telemetry proxy health-check sidecar. Required when health checks are enabled. Validation: runlayer_deploy_telemetry_proxy_image_uri must be set when Runlayer Deploy health checks are enabled. | string | "088332244652.dkr.ecr.us-east-1.amazonaws.com/runlayer/mcp-telemetry-proxy@sh... | no |
runlayer_deploy_use_shared_sg | When true, create and reuse a single shared security group for Runlayer Deploy ECS services and drop SG mutation IAM permissions. When false (default), allow Terraform to manage a per-service SG via IAM permissions. | bool | false | no |
runlayer_deploy_use_shared_task_role | When true, create and reuse a single shared IAM task role for Runlayer Deploy ECS services and drop per-deployment IAM role/policy creation permissions. When false (default), allow Terraform to manage a per-deployment task role and policy via IAM permissions. | bool | false | no |
runlayer_deploy_use_managed_policy | When true (default), attach Runlayer Deploy permissions as a managed IAM policy for migration compatibility. Set false only when managed IAM policies are prohibited, aggregate inline-policy size is verified below the AWS quota, and dense shared-role features are disabled. Validation: runlayer_deploy_use_managed_policy cannot be false when audit-log S3 archive, binary package distribution, audit-log Kinesis, audit-log SIEM export, or the hook-events pipeline is enabled because the shared role can exceed AWS’s aggregate inline-policy quota. | bool | true | no |
Sentry Relay
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
sentry_dsn | Sentry DSN override for this environment. If empty, uses value from WorkOS Vault. | string | "" | no |
sentry_audit_events_enabled | Enable emitting audit log events (tool calls, tool lists, security violations) to Sentry. Default is off to avoid unexpected quota usage. | bool | false | no |
sentry_relay_enabled | Enable Sentry Relay deployment. If false, disables relay even if credentials are available. Useful for debugging or cost optimization. | bool | true | no |
sentry_relay_upstream | Upstream Sentry endpoint for Relay | string | "https://o4509836808028160.ingest.us.sentry.io" | no |
sentry_relay_config | Sentry Relay ECS task configuration. desired_count is the Application Auto Scaling floor (target min; max is 2x), not a fixed task count: the live count belongs to autoscaling. See: https://docs.sentry.io/product/relay/operating-guidelines/ | object({ cpu = optional(number, 2048) # 2 vCPU (multi-cor... | {} | no |
OAuth broker
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
oauth_broker_url | OAuth Broker URL for OAuth flow handling | string | "" | no |
oauth_client_registration_rate_limit_per_hour | Maximum number of OAuth client registration requests per hour per IP address. Used to prevent abuse of the dynamic client registration endpoint. | number | 1000 | no |
Audit, sessions & hook events
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
enable_hook_events_pipeline | Enable hook events pipeline: Kinesis stream for ingestion, S3 bucket for Parquet storage. | bool | false | no |
hook_events_consumer_image_uri | Container image URI for the hook-events-consumer Lambda. Defaults to the CustomerDistribution ECR image tagged to match the backend image. Required when the backend image is digest-pinned. Validation: hook_events_consumer_image_uri cannot be an empty string.; hook_events_consumer_image_uri must be set when the hook-events pipeline is enabled and ecr_repositories.backend uses a digest. | string | null | no |
hook_events_alert_email | Email address for hook events pipeline alerts. Empty string disables alerts. | string | "" | no |
enable_audit_log_kinesis | Enable audit log Kinesis infrastructure: stream, IAM, and optional dormant standalone consumer service. | bool | false | no |
audit_log_write_target | Runtime audit log write target. Set to kinesis only after audit log Kinesis infra is enabled and ready. Validation: audit_log_write_target must be one of: aurora, kinesis. | string | "aurora" | no |
audit_log_kinesis_consumer_config | Standalone audit log Kinesis consumer ECS service configuration. The service is not a PGQueuer worker. Validation: audit_log_kinesis_consumer_config requires cpu >= 256, memory >= 512, and desired_count >= 0. | object({ enabled = optional(bool, false) cpu = optional(n... | {} | no |
audit_log_siem_export_kinesis_consumer_config | Standalone audit log SIEM export Kinesis consumer ECS service configuration. The service exports enriched SIEM objects from the audit-log stream and is not a PGQueuer worker. Tasks lease shards in the checkpoint table, so desired_count may exceed 1 (useful up to the stream shard count). Upgrade to the leasing module version at the current desired_count first and raise it in a later apply: a pre-leasing task overlapping leased peers causes duplicate delivery (never loss). Default 1 vCPU / 2 GB per task: 0.5 vCPU saturates near 90 audit records/s, which continuous prod-shaped load reaches. Validation: audit_log_siem_export_kinesis_consumer_config requires cpu >= 256, memory >= 512, and desired_count >= 1 when enabled. | object({ enabled = optional(bool, false) cpu = optional(n... | {} | no |
audit_log_siem_export_kinesis_s3_prefix | S3 prefix for enriched audit log SIEM exports written by the Kinesis consumer. | string | "audit-logs" | no |
agent_event_trigger_kinesis_consumer_config | Standalone agent event-trigger Kinesis consumer ECS service configuration. When enabled, it reads the audit-log Kinesis stream and dispatches matched triggers. Omitted fields default to enabling one worker when audit logs are written to Kinesis and agents are enabled. Validation: agent_event_trigger_kinesis_consumer_config requires cpu >= 256, memory >= 512, desired_count = 0 or 1, and dispatch_retry_max_age_seconds as a whole number >= 0. | object({ enabled = optional(bool) cpu = optional(number) ... | {} | no |
sessions_read_from_postgres | Flip the sessions read path to the materialized store: list from the agent_session table, detail from the session-payload S3 bucket. The writers ship with enable_hook_events_pipeline. Off = Redis read path. Instantly revertible. | bool | false | no |
session_materializer_config | Standalone session materializer Kinesis consumer ECS service configuration. The service reads the hook-events stream via polling (GetRecords) and is not a PGQueuer worker. Tasks lease hook-events shards in the checkpoint table, so desired_count may exceed 1 (useful up to the stream shard count); the compaction sweep runs on one leader task. Upgrade to the leasing module version at the current desired_count first and raise it in a later apply: a pre-leasing task overlapping leased peers causes duplicate delivery (never loss). Memory floor covers the segment-compaction fold engine (duckdb caps at 2.5GB + spill) beside the drain lane. Validation: session_materializer_config requires cpu >= 256, memory >= 3072, and desired_count >= 0. | object({ cpu = optional(number, 512) memory = optional(nu... | {} | no |
session_parquet_store_enabled | Kill switch for the session Parquet segment store, which gates session compaction. Off leaves session storage unchanged and the flag is instantly revertible. | bool | false | no |
session_parquet_delete_delay_seconds | Seconds a compacted session segment must age before the parts it replaced may be reclaimed. The 7-day default is deliberately conservative; lower it per environment only once you are confident in the compacted read path. Enforced floor is 600s. | number | 604800 | no |
enable_session_search_indexer | Standalone session-search indexer Kinesis consumer: extracts searchable text / entities / findings from hook events and writes the session-search index tables in Postgres. Requires enable_hook_events_pipeline (true without it fails the plan). Opt-in, default off. DESTRUCTIVE when flipped back to false: it removes the ECS service AND its DynamoDB checkpoint table, dead-letter bucket, IAM role, log group, alarms and dashboard (none carry prevent_destroy or deletion protection), so the stream position is lost and the next enablement starts a fresh checkpoint table at LATEST — events emitted while disabled are never indexed by the consumer. Index rows already written stay in Postgres (tables are migration-owned, not Terraform); the gap is recoverable only by replay. To merely stop processing while keeping state, use session_search_index_mode = off instead. ECS-only: no Kubernetes operator counterpart yet. | bool | false | no |
session_search_indexer_config | Standalone session-search indexer Kinesis consumer ECS service configuration (cpu / memory only). The service reads the hook-events stream via polling (GetRecords) and is not a PGQueuer worker. desired_count is not an input: it is 1 whenever session_search_index_mode is shadow or write and 0 when it is off — see session_search_index_mode for stop-vs-remove semantics. Validation: session_search_indexer_config requires cpu >= 256 and memory >= 1024 (the in-process coalescer holds up to SESSIONS_SEARCH_MAX_COALESCER_BYTES, 256 MiB by default, beside a 10 MiB poll batch). | object({ cpu = optional(number, 512) memory = optional(nu... | {} | no |
session_search_index_mode | SESSIONS_SEARCH_INDEX_MODE for the indexer task, and the non-destructive kill switch. shadow = parse + extract + coalesce + metrics, no writes (soak mode); write = real index writes, and the only mode that creates the coverage-gap alarm (the CoverageGapSessions sweep runs only when rows are actually written); off = STOP PROCESSING, KEEP STATE: the ECS service scales to desired_count 0 while the checkpoint table, DLQ bucket, IAM, log group and dashboard stay in place, and the lag and freshness alarms are not created (a parked consumer emits no heartbeat). The process also exits immediately if started in off mode, as defence-in-depth against a stray task. Flipping back to shadow/write resumes from the saved checkpoint. Instantly revertible either way. To REMOVE the service and its checkpoint state, set enable_session_search_indexer = false (destructive, see that variable). Validation: session_search_index_mode must be one of: off, shadow, write. | string | "shadow" | no |
session_search_indexer_extra_env | Passthrough env for the indexer task only, merged last onto its environment so any SESSIONS_SEARCH_* knob can be tuned without a module change (e.g. SESSIONS_SEARCH_FLUSH_SECONDS, SESSIONS_SEARCH_MAX_ROWS_PER_SECOND). Keys must start with SESSIONS_SEARCH_, or be FF_ENABLE / FF_DISABLE: the indexer’s write gate is the per-org feature flag session-search-ingest, so FF_ENABLE = “session-search-ingest” turns on index writes where per-org flags are unavailable. Scoped to this task: the backend and worker never see it. Keys the module wires itself are rejected: SESSIONS_SEARCH_INDEX_MODE (set session_search_index_mode instead — it also drives desired_count and the alarm gates, so a passthrough would run the process in one mode while Terraform provisions for another) and the stream / checkpoint-table / dead-letter-bucket pointers. Validation: session_search_indexer_extra_env keys must start with SESSIONS_SEARCH_ (or be FF_ENABLE / FF_DISABLE).; session_search_indexer_extra_env must not set SESSIONS_SEARCH_INDEX_MODE — use var.session_search_index_mode, which also drives desired_count and the alarm gates — nor the module-owned SESSIONS_SEARCH_KINESIS_STREAM_ARN / _CHECKPOINT_TABLE_NAME / _DEAD_LETTER_BUCKET pointers. | map(string) | {} | no |
enable_cold_analyzers_consumer | Standalone cold-analyzers Kinesis consumer: re-derives agent token usage facts off the hot path into a separate analytics table. Requires enable_hook_events_pipeline; null (default) follows it, false opts out (saves one always-on Fargate singleton), true fails the plan without it. ToolGuard is optional; without it, tokenizer-derived facts are skipped without stalling the stream shard. ECS-only: no Kubernetes operator counterpart. | bool | null | no |
cold_analyzers_consumer_config | Standalone cold-analyzers Kinesis consumer ECS service configuration. The service reads the hook-events stream via polling (GetRecords) and is not a PGQueuer worker. Tasks lease shards in the checkpoint table, so desired_count may exceed 1 (useful up to the stream shard count). Upgrade to the leasing module version at the current desired_count first and raise it in a later apply: a pre-leasing task overlapping leased peers causes duplicate delivery (never loss). The memory floor covers one poll batch of flattened tokenizer inputs (a GetRecords call can return ~10MB) alongside the fact rows it derives. Validation: cold_analyzers_consumer_config requires cpu >= 256, memory >= 2048, and desired_count >= 0. | object({ cpu = optional(number, 512) memory = optional(nu... | {} | no |
session_siem_export_kinesis_consumer_config | Standalone session SIEM export Kinesis consumer ECS service configuration. The service reads hook events via polling and writes gzipped NDJSON to the selected managed or external S3 bucket. Tasks lease shards in the checkpoint table, so desired_count may exceed 1 (useful up to the stream shard count). Upgrade to the leasing module version at the current desired_count first and raise it in a later apply: a pre-leasing task overlapping leased peers causes duplicate delivery (never loss). Memory floor is 2048: batches are up to 10 MiB of unslimmed session content. Validation: session_siem_export_kinesis_consumer_config requires cpu >= 256, memory >= 2048, and desired_count >= 1 when enabled. | object({ enabled = optional(bool, false) cpu = optional(n... | {} | no |
session_siem_export_external_s3_bucket | Existing external S3 bucket name for session SIEM export. When set for an enabled consumer, the module writes session objects there instead of creating a Runlayer-owned destination bucket. Validation: session_siem_export_external_s3_bucket must be a bucket name, not an ARN. | string | "" | no |
session_siem_export_external_s3_region | AWS region of the external session SIEM export bucket for direct writes. Defaults to the ECS deployment region. Ignored for assume-role exports. Validation: session_siem_export_external_s3_region must be a valid AWS region (e.g. us-east-1). | string | "" | no |
session_siem_export_external_s3_kms_key_arn | Optional KMS key ARN for direct writes to an external session SIEM export bucket using SSE-KMS. Only valid without session_siem_export_assume_role_arn; for assume-role exports, the customer role must hold KMS permissions. Validation: session_siem_export_external_s3_kms_key_arn must be a KMS key ARN. | string | "" | no |
session_siem_export_assume_role_arn | Optional customer-owned IAM role ARN to assume before writing session SIEM export objects to the destination bucket. Validation: session_siem_export_assume_role_arn must be an IAM role ARN. | string | "" | no |
session_siem_export_external_id | ExternalId value used when assuming session_siem_export_assume_role_arn. | string | "" | no |
session_siem_export_s3_prefix | S3 key prefix for session SIEM export objects. Leading/trailing slashes are trimmed. Set to an empty string to write at bucket root. | string | "sessions" | no |
enable_audit_payload_offload | Enable S3 offloading for large audit log payloads (>10KB). When enabled, creates an S3 bucket and grants backend access to store/retrieve large tool call results. | bool | true | no |
enable_audit_log_spool | Route audit log emission through a local-disk spool + in-process tailer so proxy requests do not wait on audit delivery (S3 offload, Aurora/Kinesis write, Sentry emit). Records are delivered within seconds; the spool lives on task ephemeral storage (Fargate-encrypted at rest). Validation: The audit spool’s shutdown drain window is backend_stop_timeout_seconds - 30s; at <= 30s it is zero and undelivered audit records are lost on every deploy. Raise backend_stop_timeout_seconds above 30 or set enable_audit_log_spool = false. | bool | true | no |
audit_log_spool_max_bytes | App-level audit-spool byte budget, rendered as AUDIT_LOG_SPOOL_MAX_BYTES on the backend task only (parity with the k8s chart/CRD maxBytes knob). null (default) omits the env and the backend’s 512MB default applies. The spool (and its dead-letter quarantine, which shares this budget) lives on the backend task’s ephemeral storage — when raising this well past the default, size backend task ephemeral storage accordingly. Validation: audit_log_spool_max_bytes must be a positive integer byte count no larger than 2^62 (the k8s paths cap it identically so size derivations can never overflow; the backend setting is an int, so a fractional value would fail task startup).; audit_log_spool_max_bytes must be smaller than backend_ephemeral_storage_size_gib — the spool (and its dead-letter quarantine) lives on the backend task’s ephemeral disk, which also holds images, logs, and other task data. | number | null | no |
enable_audit_log_siem_export | Enable audit log SIEM export. By default this creates/manages a Runlayer S3 bucket unless audit_log_siem_export_external_s3_bucket is set. | bool | false | no |
audit_log_siem_export_external_s3_bucket | Existing external S3 bucket name for audit log SIEM export. When set with enable_audit_log_siem_export, the module writes SIEM objects there instead of creating a Runlayer-owned SIEM export bucket. Validation: audit_log_siem_export_external_s3_bucket must be a bucket name, not an ARN. | string | "" | no |
audit_log_siem_export_external_s3_region | AWS region of the external SIEM export S3 bucket for direct bucket writes. Defaults to the ECS deployment region. Ignored for assume-role exports. Validation: audit_log_siem_export_external_s3_region must be a valid AWS region (e.g. us-east-1). | string | "" | no |
audit_log_siem_export_external_s3_kms_key_arn | Optional KMS key ARN for direct writes to an external audit log SIEM export S3 bucket using SSE-KMS. Only valid without audit_log_siem_export_assume_role_arn; for assume-role exports, the customer role must hold KMS permissions. Validation: audit_log_siem_export_external_s3_kms_key_arn must be a KMS key ARN. | string | "" | no |
audit_log_siem_export_assume_role_arn | Optional customer-owned IAM role ARN to assume before writing audit log SIEM export objects to the destination S3 bucket. Validation: audit_log_siem_export_assume_role_arn must be an IAM role ARN. | string | "" | no |
audit_log_siem_export_external_id | ExternalId value used when assuming audit_log_siem_export_assume_role_arn. | string | "" | no |
audit_log_siem_export_s3_prefix | S3 key prefix for audit log SIEM export objects. Leading/trailing slashes are trimmed. Set to an empty string to write at bucket root. | string | "audit-logs" | no |
enable_audit_log_s3_archive | Enable audit log S3 archive bucket, ECS task, and schedule. | bool | true | no |
audit_log_archive_task_config | ECS task config for the audit log S3 archive job. 4GB memory is temporary backfill headroom to avoid OOMs on large legacy audit payloads. Validation: audit_log_archive_task_config requires cpu >= 256 and memory >= 512. | object({ cpu = optional(number, 512) memory = optional(nu... | {} | no |
audit_log_archive_hot_window_days | Days to keep hot in Aurora before archiving to S3. 0 archives all data (sandbox/test only). | number | 90 | no |
audit_log_siem_export_read_role_arns | IAM role ARNs allowed to read and list audit log SIEM export objects from the Runlayer-managed bucket. Ignored when audit_log_siem_export_external_s3_bucket is set. | list(string) | [] | no |
session_siem_export_read_role_arns | Customer-side IAM role ARNs granted read and list access to the Runlayer-managed session SIEM export bucket. Ignored when session_siem_export_external_s3_bucket is set. | list(string) | [] | no |
Secrets & misc features
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
distribution_api_key | Optional Distribution API reader credential for the flagd OpenFeature provider. (sensitive) | string | (sensitive — omitted) | no |
secret_key | Existing non-empty application secret key. Must be provided with master_salt. Omit both to generate new values. Validation: secret_key and master_salt must both be non-empty or both be omitted. (sensitive) | string | null | no |
master_salt | Existing non-empty application master salt. Must be provided with secret_key. Omit both to generate new values. (sensitive) | string | null | no |
runlayer_download_token | Token sent as x-runlayer-download-token to fetch binary release manifests from downloads.runlayer.com. Must match one of the downloads distribution’s download_tokens. Empty disables binary release discovery. (sensitive) | string | (sensitive — omitted) | no |
runlayer_downloads_base_url | Base URL for the binary distribution CDN that serves release manifests. | string | "https://downloads.runlayer.com" | no |
enable_dashboard | Enable CloudWatch infrastructure dashboard | bool | true | no |
event_loop_monitor_interval_s | Event loop monitor polling interval in seconds Validation: event_loop_monitor_interval_s must be null or >= 0. Set 0 to disable. | number | null | no |
event_loop_warn_threshold_ms | Event loop block duration (ms) that triggers CloudWatch EMF emission. Validation: event_loop_warn_threshold_ms must be null or > 0. | number | null | no |
event_loop_slow_callback_threshold_s | Threshold (seconds) for asyncio slow callback warnings. Validation: event_loop_slow_callback_threshold_s must be null or > 0. | number | null | no |
event_loop_slow_callback_on_s | Duration (seconds) to enable slow callback logging per duty cycle. Validation: event_loop_slow_callback_on_s must be null or > 0. | number | null | no |
enable_tool_output_offload | Enable dedicated S3 bucket for tool output offloading (>10KB). Separate from audit payload storage. | bool | false | no |
enable_binary_package_distribution | Enable the S3 cache bucket for binary packages mirrored from downloads.runlayer.com. Release discovery and cache population require runlayer_download_token; serving cached artifacts does not. | bool | false | no |
data_export_bucket_arn | ARN of the external drop-zone S3 bucket that hook-events parquet is pushed to. ARN string only (never a foreign-account principal) so applies never depend on the destination account’s state. Empty disables the whole sync stack. Validation: data_export_bucket_arn must be an S3 bucket ARN (arn:aws:s3:::bucket-name) or empty. | string | "" | no |
data_export_sync_task_config | ECS task config for the Data export sync job. Defaults size the task for the in-tenant scrub (in-process NER model plus parquet rewrite buffers). The NER scrub is single-core CPU-bound, so workers > 1 scrubs files in that many processes, each with its own model (~1 GB each, plus ~1 GB for the parent); raise cpu and memory with it. Validation: data_export_sync_task_config requires cpu >= 256 and memory >= 512. data_export_sync_task_config.workers > 1 needs cpu >= workers x 1024 and memory >= (workers + 1) x 1024 MiB (~1 GB per scrub process plus the parent). | object({ cpu = optional(number, 1024) memory = optional(n... | {} | no |