feat(terraform): support multiple EFS volumes per game server#37
Conversation
Each game server can now declare one or more EFS mount points via a
`volumes` list instead of the single `efs_path` string. Each entry
produces its own EFS access point rooted at `/${game}/${name}` and a
matching ECS volume/mountPoints pair, enabling games that require
multiple distinct container paths.
Breaking change: all existing `efs_path` entries in terraform.tfvars
must be migrated to the `volumes` list format. EFS access point paths
change (e.g. `/palworld` → `/palworld/saves`), so existing save data
on EFS may need to be moved to the new subdirectory after apply.
Closes #36
https://claude.ai/code/session_01SkWgmzBi8EPkSvpf3shwno
There was a problem hiding this comment.
Pull request overview
Refactors the Terraform EFS configuration for game servers to support multiple EFS-backed mount points per game by replacing the single efs_path setting with a volumes list, and updates docs/examples accordingly.
Changes:
- Update
game_serversvariable schema fromefs_pathtovolumes[](name,container_path) and refresh example configs. - Add a
local.game_volumesflattening map to create one EFS access point per game-volume pair and update ECS task definition volume/mountPoint generation. - Update setup and Terraform component docs to describe the new
volumesstructure and access point isolation model.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| terraform/variables.tf | Updates game_servers schema to volumes[] and adjusts defaults. |
| terraform/terraform.tfvars.example | Updates example config to the new volumes[] format with explanatory comments. |
| terraform/main.tf | Implements per-volume EFS access points and dynamic ECS volumes/mount points. |
| docs/docs/setup.md | Updates setup guide examples and rules to refer to volumes[]. |
| docs/docs/components/terraform.md | Updates variable documentation to reflect volumes[] and per-volume access point behavior. |
…umes refactor
The efs_access_points output was iterating over aws_efs_access_point.game
which is now keyed by "${game}-${volume.name}", producing keys like
"palworld-saves" instead of "palworld". FileManagerService looks up
outputs.efs_access_points[game] by bare game name, so it would always
get undefined and fail to start a file manager session.
Fix by building the output map directly from var.game_servers, using
each game's first volume to look up its access point — preserving the
game => access_point_id shape the app expects.
https://claude.ai/code/session_01SkWgmzBi8EPkSvpf3shwno
There was a problem hiding this comment.
Pull request overview
Refactors the Terraform EFS + ECS task definition wiring to allow multiple EFS-backed mount points per game server, updating docs/examples accordingly.
Changes:
- Replaced per-game
efs_pathwithvolumes[](name + container mount path) ingame_servers. - Creates one EFS access point per game-volume pair and mounts all configured volumes into the container.
- Updates Terraform outputs and documentation/examples to match the new multi-volume model.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| terraform/variables.tf | Updates game_servers schema to use volumes[] and adjusts defaults. |
| terraform/terraform.tfvars.example | Updates example config and adds guidance for volumes[]. |
| terraform/outputs.tf | Keeps efs_access_points output shape (game → AP) by selecting the first volume’s AP. |
| terraform/main.tf | Adds local.game_volumes, refactors EFS access point creation and ECS volume/mount configuration. |
| docs/docs/setup.md | Updates setup instructions from efs_path to volumes[] with new semantics. |
| docs/docs/components/terraform.md | Updates variable documentation to reflect volumes[] and per-volume access points. |
| # Flattened map of all game-volume pairs, keyed by "${game}-${volume.name}". | ||
| # Used to create one EFS access point per volume entry. | ||
| game_volumes = { | ||
| for pair in flatten([ | ||
| for game, cfg in var.game_servers : [ | ||
| for vol in cfg.volumes : { | ||
| key = "${game}-${vol.name}" | ||
| game = game | ||
| name = vol.name | ||
| container_path = vol.container_path | ||
| } | ||
| ] | ||
| ]) : pair.key => pair | ||
| } |
There was a problem hiding this comment.
Declined — the cross-game collision requires two games whose names and volume names split across - in the exact same way, which is a purely theoretical concern with normal identifiers like palworld, satisfactory, saves, config. A duplicate vol.name within the same game would cause Terraform to error on the duplicate for key, not silently overwrite — so that case is already caught loudly.
Generated by Claude Code
| description = "Map of game name → first volume's EFS access point ID (consumed by FileManagerService)" | ||
| value = { | ||
| for game, cfg in var.game_servers : | ||
| game => aws_efs_access_point.game["${game}-${cfg.volumes[0].name}"].id |
There was a problem hiding this comment.
Fixed in f01fc01 — added a validation block to the game_servers variable enforcing at least one volume entry with non-empty name and container_path. This fires at variable evaluation time with a clear message rather than producing an obscure Invalid index during plan.
Generated by Claude Code
An empty volumes list would cause an obscure "Invalid index" error at plan time when outputs.tf accesses cfg.volumes[0]. A validation block gives a clear error message at variable evaluation time instead. https://claude.ai/code/session_01SkWgmzBi8EPkSvpf3shwno
Summary
Refactored the EFS volume configuration to support multiple mount points per game server, replacing the single
efs_pathfield with a flexiblevolumeslist. Each volume entry now creates its own EFS access point with isolated directory structure.Key Changes
efs_path: stringwithvolumes: list(object({ name, container_path }))ingame_serversvariable definitiongame_volumeslocal that flattens game-volume pairs into a keyed map (${game}-${volume.name}) for EFS access point creation/${game}/${volume_name}instead of/${game}volumeblock todynamic "volume"block that iterates overeach.value.volumes, creating one volume mount per configured volumemountPointsfrom a single static entry to a dynamic list comprehension that maps each volume to its container pathvolumesstructure and explain the per-volume access point isolationImplementation Details
game_volumesensures each game-volume combination gets a unique key while preserving all necessary metadata (game name, volume name, container path)volumeslisthttps://claude.ai/code/session_01SkWgmzBi8EPkSvpf3shwno