Unreal Horde¶
Unreal Engine Horde is a set of services supporting workflows Epic uses to develop Fortnite, Unreal Engine, and other titles. This module deploys the Unreal Engine Horde server on AWS Elastic Container Service using the image available from the Epic Games Github organization (requires Epic Games organization membership). Unreal Engine Horde relies on a Redis cache and a MongoDB compatible database. This module provides these services by provisioning an Amazon Elasticache with Redis OSS Compatibility cluster and an Amazon DocumentDB cluster.
Check out this video from Unreal Fest 2024 to learn more about the Unreal Horde module:
Deployment Architecture¶

Prerequisites¶
Unreal Engine Horde is only available through the Epic Games Github organization's package registry or the Unreal Engine source code. In order to get access to this software you will need to join the Epic Games organization on Github and accept the Unreal Engine EULA.
Examples¶
For example configurations, please see the examples.
Server Configuration Delivery¶
The Horde server reads its startup settings from /app/Data/server.json. An init container (unreal-horde-init) renders this file before the server starts, so the settings the current Horde image honors (config path, Perforce connection) are delivered as configuration files rather than environment variables.
Perforce connection¶
To connect Horde to a Perforce server, set p4_port and provide the credentials through AWS Secrets Manager:
-
Create a Secrets Manager secret containing a JSON object with the Perforce username and password Horde should connect as:
{"username": "horde-service-user", "password": "example-password"} -
Pass the secret's ARN to the module via
p4_credentials_secret_arn.
The credentials are injected into the init container at startup using ECS-native Secrets Manager integration (the task execution role is granted secretsmanager:GetSecretValue on the secret automatically). The init container substitutes them in-memory — they never appear in the ECS task definition, CloudTrail, container logs, or Terraform state.
The init container substitutes the credentials into two places, using the __P4_USERNAME__ / __P4_PASSWORD__ placeholder tokens:
server.json(rendered by the module) underplugins.build.perforce— the Horde server's Perforce connection.globals.json(supplied by you viaconfig_globals_json) — see below.
Horde configuration (globals.json)¶
The config_path variable controls the Horde server's configPath setting:
- Set
config_path = "globals.json"together withconfig_globals_json(a JSON string) to have the init container write your Horde configuration to/app/Data/globals.json. - Set
config_pathto a Perforce depot path (e.g."//UE/Main/Config/globals.json") to have Horde load its configuration from your depot instead.
Perforce cluster credentials in globals.json¶
Horde 5.5 resolves the Perforce cluster used for stream polling from the top-level perforceClusters array in globals.json, and it does not fall back to server.json for that path. If your perforceClusters entry omits credentials, stream polling authenticates as the wrong user (or the OS default) and fails.
To deliver the Perforce credentials to a cluster without ever placing the password in your config string or Terraform state, put the same placeholder tokens inside a cluster's credentials entry. The init container substitutes them from p4_credentials_secret_arn at startup:
"perforceClusters": [
{
"name": "default",
"serviceAccount": "svc-horde",
"servers": [ { "serverAndPort": "ssl:perforce.example.com:1666" } ],
"credentials": [
{ "userName": "__P4_USERNAME__", "password": "__P4_PASSWORD__" }
]
}
]
The credentials substitution is a no-op when the placeholders are absent, so existing config_globals_json values are unaffected. For deploy diagnostics the init container prints globals.json to the [INIT] CloudWatch log before substituting the password, so __P4_PASSWORD__ appears as a literal placeholder in the log and the injected secret is never written to CloudWatch.
Requirements¶
| Name | Version |
|---|---|
| terraform | >= 1.0 |
| aws | ~> 6.6 |
| random | ~> 3.7 |
Providers¶
| Name | Version |
|---|---|
| aws | ~> 6.6 |
| random | ~> 3.7 |
Modules¶
No modules.
Resources¶
Inputs¶
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| certificate_arn | The TLS certificate ARN for the Unreal Horde load balancer. | string |
n/a | yes |
| fully_qualified_domain_name | The fully qualified domain name where your Unreal Engine Horde server will be available. This agents will use this to enroll. | string |
n/a | yes |
| unreal_horde_service_subnets | A list of subnets to deploy the Unreal Horde service into. Private subnets are recommended. | list(string) |
n/a | yes |
| vpc_id | The ID of the existing VPC you would like to deploy Unreal Horde into. | string |
n/a | yes |
| admin_claim_type | The claim type for administrators. | string |
null |
no |
| admin_claim_value | The claim value for administrators. | string |
null |
no |
| agent_dotnet_runtime_version | The dotnet-runtime-{} package to install (see your engine version's release notes for supported version) | string |
"6.0" |
no |
| agent_enable_long_paths | Enable NTFS long-path support (LongPathsEnabled=1) on Windows agents. Recommended for from-source Unreal Engine builds, which routinely exceed MAX_PATH. | bool |
false |
no |
| agent_uba_compute_ports | Inbound TCP port range to open in the host Windows Firewall for UBA (Unreal Build Accelerator) distributed compile workers, e.g. "7000-7010". The agent security group must also allow this range. Set to null to skip the firewall rule. | string |
null |
no |
| agent_uba_horde_pool | If set, writes a UBT BuildConfiguration.xml on Windows agents that enables UBA-over-Horde, targeting this Horde pool name (must match a pool in your globals.json, e.g. "Win-UE5"). Leave null to not configure UBA-over-Horde. | string |
null |
no |
| agent_uba_max_workers | MaxWorkers value for the UBT BuildConfiguration.xml |
number |
4 |
no |
| agent_working_dir | Working directory for the Horde agent on Windows. A SHORT path (e.g. "C:\H") keeps deeply-nested engine-plugin response-file paths under MAX_PATH for link.exe/cl.exe. Written to the durable agent.json so it survives agent self-upgrades. Set to null to leave the agent default. | string |
null |
no |
| agents | Configures autoscaling groups to be used as build agents by Unreal Engine Horde. | map(object({ |
{} |
no |
| auth_method | The authentication method for the Horde server. | string |
null |
no |
| cluster_name | The name of the cluster to deploy the Unreal Horde into. Defaults to null and a cluster will be created. | string |
null |
no |
| config_globals_json | JSON string content for the Horde globals.json configuration file. When non-empty it is written to /app/Data/globals.json by the init container; pair it with config_path = "globals.json". The init container substitutes the __P4_USERNAME__ / __P4_PASSWORD__ placeholder tokens (from p4_credentials_secret_arn) into this content at startup, so you can place them inside a perforceClusters[].credentials entry without the password ever landing in Terraform state or the task definition. Substitution is a no-op when the placeholders are absent. Leave empty to manage config another way (e.g. a Perforce config_path). | string |
"" |
no |
| config_path | Value for the Horde server's configPath setting (written to /app/Data/server.json). Use "globals.json" to load the file rendered from config_globals_json into /app/Data, or a Perforce path (e.g. "//UE/Main/...") to load config from the depot. |
string |
null |
no |
| container_api_port | The container port for the Unreal Horde web server. | number |
5000 |
no |
| container_cpu | The CPU allotment for the Unreal Horde container. | number |
1024 |
no |
| container_grpc_port | The container port for the Unreal Horde GRPC channel. | number |
5002 |
no |
| container_memory | The memory allotment for the Unreal Horde container. | number |
4096 |
no |
| container_name | The name of the Unreal Horde container. | string |
"unreal-horde-container" |
no |
| create_external_alb | Set this flag to true to create an external load balancer for Unreal Horde. | bool |
true |
no |
| create_internal_alb | Set this flag to true to create an internal load balancer for Unreal Horde. | bool |
true |
no |
| create_unreal_horde_default_policy | Optional creation of Unreal Horde default IAM Policy. Default is set to true. | bool |
true |
no |
| create_unreal_horde_default_role | Optional creation of Unreal Horde default IAM Role. Default is set to true. | bool |
true |
no |
| create_unreal_horde_recycle_policy | Optional creation of Unreal Horde IAM Policy allowing usage of the AwsReuse/AwsRecycle fleet manager. | bool |
false |
no |
| custom_cache_connection_config | The redis-compatible connection configuration that Horde should use. | string |
null |
no |
| custom_unreal_horde_role | ARN of the custom IAM Role you wish to use with Unreal Horde. | string |
null |
no |
| database_connection_string | The database connection string that Horde should use. | string |
null |
no |
| debug | Set this flag to enable ECS execute permissions on the Unreal Horde container and force new service deployments on Terraform apply. | bool |
false |
no |
| desired_container_count | The desired number of containers running Unreal Horde. | number |
1 |
no |
| docdb_backup_retention_period | Number of days to retain backups for DocumentDB cluster. | number |
7 |
no |
| docdb_instance_class | The instance class for the Horde DocumentDB cluster. | string |
"db.t4g.medium" |
no |
| docdb_instance_count | The number of instances to provision for the Horde DocumentDB cluster. | number |
2 |
no |
| docdb_master_password | Master password created for DocumentDB cluster. | string |
"mustbeeightchars" |
no |
| docdb_master_username | Master username created for DocumentDB cluster. | string |
"horde" |
no |
| docdb_preferred_backup_window | The preferred window for DocumentDB backups to be created. | string |
"07:00-09:00" |
no |
| docdb_skip_final_snapshot | Flag for whether a final snapshot should be created when the cluster is destroyed. | bool |
true |
no |
| docdb_storage_encrypted | Configure DocumentDB storage at rest. | bool |
true |
no |
| elasticache_cluster_count | Number of cache cluster to provision in the Elasticache cluster. | number |
2 |
no |
| elasticache_engine | The engine to use for ElastiCache (redis or valkey) | string |
"redis" |
no |
| elasticache_node_count | Number of cache nodes to provision in the Elasticache cluster. | number |
1 |
no |
| elasticache_node_type | The type of nodes provisioned in the Elasticache cluster. | string |
"cache.t4g.micro" |
no |
| elasticache_port | The port for the ElastiCache cluster. | number |
6379 |
no |
| elasticache_redis_engine_version | The version of the Redis engine to use. | string |
"7.0" |
no |
| elasticache_redis_parameter_group_name | The name of the Redis parameter group to use. | string |
"default.redis7" |
no |
| elasticache_snapshot_retention_limit | The number of Elasticache snapshots to retain. | number |
5 |
no |
| elasticache_valkey_engine_version | The version of the ElastiCache engine to use. | string |
"7.2" |
no |
| elasticache_valkey_parameter_group_name | The name of the Valkey parameter group to use. | string |
"default.valkey7" |
no |
| enable_new_agents_by_default | Set this flag to automatically enable new agents that enroll with the Horde Server. | bool |
false |
no |
| enable_unreal_horde_alb_access_logs | Enables access logging for the Unreal Horde ALB. Defaults to true. | bool |
true |
no |
| enable_unreal_horde_alb_deletion_protection | Enables deletion protection for the Unreal Horde ALB. Defaults to true. | bool |
false |
no |
| environment | The current environment (e.g. Development, Staging, Production, etc.). This will tag ressources and set ASPNETCORE_ENVIRONMENT variable. | string |
"Development" |
no |
| existing_security_groups | A list of existing security group IDs to attach to the Unreal Horde load balancer. | list(string) |
[] |
no |
| github_credentials_secret_arn | A secret containing the Github username and password with permissions to the EpicGames organization. | string |
null |
no |
| image | The Horde Server image to use in the ECS service. | string |
"ghcr.io/epicgames/horde-server:latest-bundled" |
no |
| name | The name attached to Unreal Engine Horde module resources. | string |
"unreal-horde" |
no |
| oidc_audience | The audience used for validating externally issued tokens. | string |
null |
no |
| oidc_authority | The authority for the OIDC authentication provider used. | string |
null |
no |
| oidc_client_id | The client ID used for authenticating with the OIDC provider. | string |
null |
no |
| oidc_client_secret | The client secret used for authenticating with the OIDC provider. | string |
null |
no |
| oidc_signin_redirect | The sign-in redirect URL for the OIDC provider. | string |
null |
no |
| p4_credentials_secret_arn | ARN of an AWS Secrets Manager secret containing the Perforce credentials the Horde server connects with, as a JSON object: {"username": "...", "password": "..."}. The credentials are fetched at container startup and injected into /app/Data/server.json under plugins.build.perforce (and into any __P4_USERNAME__/__P4_PASSWORD__ placeholders in config_globals_json) - they never appear in the ECS task definition or Terraform state. Required when p4_port is set. | string |
null |
no |
| p4_port | The Perforce server to connect to. | string |
null |
no |
| project_prefix | The project prefix for this workload. This is appeneded to the beginning of most resource names. | string |
"cgd" |
no |
| tags | Tags to apply to resources. | map(any) |
{ |
no |
| unreal_horde_alb_access_logs_bucket | ID of the S3 bucket for Unreal Horde ALB access log storage. If access logging is enabled and this is null the module creates a bucket. | string |
null |
no |
| unreal_horde_alb_access_logs_prefix | Log prefix for Unreal Horde ALB access logs. If null the project prefix and module name are used. | string |
null |
no |
| unreal_horde_cloudwatch_log_retention_in_days | The log retention in days of the cloudwatch log group for Unreal Horde. | string |
365 |
no |
| unreal_horde_external_alb_subnets | A list of subnets to deploy the Unreal Horde load balancer into. Public subnets are recommended. | list(string) |
[] |
no |
| unreal_horde_internal_alb_subnets | A list of subnets to deploy the Unreal Horde internal load balancer into. Private subnets are recommended. | list(string) |
[] |
no |
Outputs¶
| Name | Description |
|---|---|
| agent_security_group_id | n/a |
| external_alb_dns_name | n/a |
| external_alb_sg_id | n/a |
| external_alb_zone_id | n/a |
| internal_alb_dns_name | n/a |
| internal_alb_sg_id | n/a |
| internal_alb_zone_id | n/a |
| service_security_group_id | n/a |
