> ## Documentation Index
> Fetch the complete documentation index at: https://www.ravion.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# EC2 Service

> Runs supervised workloads on a stable EC2 Auto Scaling Group, with optional shared ALB routing and switchable container or manual in-place deploys.

**Type:** `rvn-ec2-service` · **Latest version:** `0.1.1`

## Dependencies and consumers

```mermaid theme={null}
flowchart BT
  rvn_ec2_service["<strong>EC2 Service</strong>"] --> rvn_aws_network["rvn-aws-network"]
  rvn_ec2_service -.-> rvn_aws_alb["rvn-aws-alb"]
  rvn_ec2_service -.-> rvn_ecs_cluster["rvn-ecs-cluster"]
  rvn_ec2_service -.-> rvn_efs["rvn-efs"]
  rvn_aws_alb --> rvn_aws_network
  rvn_aws_alb -.-> rvn_acm_certificate["rvn-acm-certificate"]
  rvn_ecs_cluster --> rvn_aws_network
  rvn_ecs_cluster -.-> rvn_acm_certificate
  rvn_efs --> rvn_aws_network

  class rvn_ec2_service current
  class rvn_aws_network,rvn_aws_alb,rvn_ecs_cluster,rvn_efs dependency
  class rvn_acm_certificate upstream

  classDef current fill:#C4B5FD,stroke:#8B5CF6,color:#3B0764
  classDef dependency fill:#FAF8FF,stroke:#DDD6FE,color:#6D28D9
  classDef consumer fill:#F3F4F6,stroke:#9CA3AF,color:#4B5563
  classDef upstream fill:#F8FAFC,stroke:#CBD5E1,color:#94A3B8
  linkStyle 4,5,6,7,8 stroke:#CBD5E1,color:#94A3B8

  click rvn_aws_network "/module-definitions/catalog/rvn-aws-network" "Open rvn-aws-network docs"
  click rvn_aws_alb "/module-definitions/catalog/rvn-aws-alb" "Open rvn-aws-alb docs"
  click rvn_ecs_cluster "/module-definitions/catalog/rvn-ecs-cluster" "Open rvn-ecs-cluster docs"
  click rvn_efs "/module-definitions/catalog/rvn-efs" "Open rvn-efs docs"
  click rvn_acm_certificate "/module-definitions/catalog/rvn-acm-certificate" "Open rvn-acm-certificate docs"
```

*Every dependency input can be specified manually to reference existing external infrastructure rather than a Ravion module.*

## Readme

Runs supervised workloads on a stable EC2 Auto Scaling Group, with optional shared ALB routing and switchable container or manual in-place deploys.

### Overview

The EC2 Service module deploys supervisord-managed workloads across an EC2 Auto Scaling Group. Deploys update the existing hosts in place instead of creating a new task or instance for every release. Container mode replaces a Docker container on each host; Manual mode runs host-level preparation commands and then starts a foreground command. You can switch modes without replacing the group.

Ravion provisions the launch template, Auto Scaling Group, instance role and security group, SSM deploy document, app log group, and optional target group and listener rule. Web services can attach to a standalone Application Load Balancer or reuse the public or private ALB from an ECS cluster. The selected network and load balancer must use the same AWS account, region, and VPC. The Auto Scaling Group can still terminate instances because of scaling, health failures, or operator action. Root and data EBS volumes survive releases, but not instance termination.

Terraform source: [flightcontrolhq/modules/compute/ec2\_service](https://github.com/flightcontrolhq/modules/tree/rvn-ec2-service@0.1.1/compute/ec2_service)

### Use cases

| Scenario                                          | Why this module fits                                                                  | Example                                                                    |
| ------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
| Existing host-installed application               | Manual deploys can update files and dependencies directly on every host               | A legacy Node, Ruby, or JVM service that is not packaged as a container    |
| Host customization is part of the workload        | A custom AMI and Additional user data can install agents, libraries, or host services | A vendor runtime that needs an OS package or host monitoring agent         |
| Disposable per-host state should survive releases | In-place deploys retain each instance's encrypted root and optional data volume       | Model files, build artifacts, or a rebuildable local cache                 |
| A container workload needs stable hosts           | Container mode replaces the app container without replacing the EC2 instance          | A licensed service tied to host identity, when its license permits scaling |
| Background processing without HTTP routing        | Web service can be disabled, leaving a supervised worker group                        | A queue consumer that specifically needs host access or local cache        |
| Several services share one ALB                    | Each service owns a target group and host/path listener rule                          | `api.example.com` and `admin.example.com` on one shared ALB                |

Do not treat per-instance EBS as durable application storage. Use EFS for shared files that must survive instance replacement, and use a managed database for relational or transactional data.

### EC2 Service or ECS?

For a normal stateless containerized web service or worker, prefer an ECS Web Service, ECS Worker Service, or ECS Network Service. ECS gives you immutable task revisions, scheduler-managed replacement, sidecars, Fargate or shared EC2 capacity, and rolling or traffic-shift deployment strategies.

Choose EC2 Service only when a concrete host-level requirement outweighs those ECS benefits:

| Decision          | EC2 Service                                                             | ECS deployment                                                              |
| ----------------- | ----------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| Release model     | Updates the app on existing hosts through SSM                           | Starts new task revisions and replaces old tasks                            |
| Packaging         | Container image or host-level manual commands                           | Container image                                                             |
| Host access       | Direct instance access, custom AMI, and bootstrap script                | Tasks are the deployment unit; hosts are abstracted or cluster-managed      |
| Process model     | One supervised app per instance group                                   | App container plus optional sidecars and release tasks                      |
| Capacity          | Dedicated Auto Scaling Group for this app                               | Fargate, Fargate Spot, or shared/dedicated ECS EC2 capacity                 |
| Local storage     | EBS survives deploys but is lost with an instance                       | Task-local storage is ephemeral; EFS can survive task replacement           |
| Deployment safety | Sequential in-place updates; a one-instance service has an interruption | Rolling, blue/green, linear, or canary options, depending on the ECS module |

Selecting EC2 capacity for an ECS service does not make it equivalent to this module. ECS on EC2 still deploys replaceable tasks through the ECS scheduler. This module deploys directly to stable hosts and gives the application the whole instance.

### Deploy types

| Deploy type | Build behavior                          | On each instance                                                                    |
| ----------- | --------------------------------------- | ----------------------------------------------------------------------------------- |
| Container   | Dockerfile, Railpack, or image registry | Pull the selected image, replace the app container, and run it under supervisord    |
| Manual      | Build disabled                          | Run Deploy commands as root on every host, then run Start command under supervisord |

Instances are prepared for both modes at launch. Supervisord owns the long-running process and restarts it after an unexpected exit. In Container mode, Start command override replaces the image CMD but preserves its ENTRYPOINT. In Manual mode, the start command must remain in the foreground and should explicitly drop root privileges when the app should run as another user.

### Build sources

| Build source             | Build output                                                                        | Deploy input                         |
| ------------------------ | ----------------------------------------------------------------------------------- | ------------------------------------ |
| Dockerfile               | Build from the selected Git repository and push to the service ECR repository       | Image tag or digest                  |
| Railpack                 | Detect and build the app from the selected Git repository, then push to service ECR | Image tag or digest                  |
| Pull from image registry | Skip the build and use Image repository from the module                             | Tag or digest within that repository |

Build settings apply only to Container mode. Manual mode disables the build pipeline and does not create an ECR repository. Built-image repositories scan images on push by default. Registry username/password credentials are not supported. Public images work directly; same-region private ECR requires repository permissions for the generated `<service-name>-instance` role.

### Worked examples

#### Host-installed application

Use Manual mode when the release genuinely needs to update the host rather than replace a container. For example, bootstrap an `app` user and an initial checkout with Additional user data, then configure:

```text theme={null}
Deploy type: Manual
Deploy commands:
  runuser -u app -- git -C /srv/orders pull --ff-only
  runuser -u app -- bash -c 'cd /srv/orders && ./bin/install-dependencies'
  runuser -u app -- bash -c 'cd /srv/orders && ./bin/build-assets'
Start command:
  exec runuser -u app -- /srv/orders/bin/server
```

Every Deploy command runs on every instance, so keep commands repeatable and do not put a cluster-wide one-time migration in this list. Select Git repository to have Ravion check out a clean authenticated source before running the commands, or leave it blank when the commands manage their own source or artifacts. Deploy commands and the Start command run from Source base path when source is configured. Use Container mode for digest-addressed image releases; use ECS when you need scheduler-managed rollback or traffic shifting.

#### Container that needs host customization

Choose Container mode with a custom AMI or Additional user data when the app is containerized but also needs host software that ECS does not model for the service. A typical web configuration uses at least two instances, App port `3000`, Health check path `/health`, and host or path rules on a shared ALB. Use Dockerfile or Railpack for Ravion builds, or Pull from image registry when an external pipeline already publishes the image.

If the requirement is only "run my container," use ECS instead. The EC2 choice should come from a real host dependency, stable-host requirement, or manual operational constraint.

#### Worker with a rebuildable local cache

Turn Web service off, keep Container mode, and enable a Data volume mounted at a path such as `/var/cache/app`. This is appropriate when the worker can recreate the cache after an instance is replaced. It is not appropriate for the only copy of jobs, uploads, or database data.

### Load balancing and worker mode

With Web service enabled, select a shared Ravion Application Load Balancer. The service creates an HTTP instance target group and a listener rule on the ALB's HTTPS listener when available, otherwise its HTTP listener. Route traffic with Domain host rules, Path rules, or both; when both are empty, the rule matches `/*`.

Several EC2 services (and other services) can attach to the same load balancer with different host or path rules. Slow start can ramp traffic to newly registered instances, and sticky sessions can use either an ALB-managed cookie or an application cookie.

Allowed CIDR blocks permit direct access to the app port on the instances. This bypasses load balancer TLS termination, WAF, authentication, and access logging, so leave the list empty unless direct access is required.

Turn Web service off for worker groups. No target group, listener rule, ALB health check, or local HTTP deploy gate is configured.

### Deploys

Container deploys run through an SSM command document created by the module. On each instance, the deploy:

1. Rebuilds the app environment file and fetches configured secret values on the instance.
2. Pulls the selected image before interrupting the running app.
3. For a web service with another registered target, deregisters and drains this instance.
4. Stops the prior process, replaces the app container, and starts it under supervisord.
5. Polls the local health path for an HTTP response below 400, failing that instance's deploy otherwise.
6. Re-registers a drained instance and waits for the ALB to mark it in service.

A web service with only one registered target skips draining because there is nowhere to move traffic; replacing its process still causes an interruption. Use at least two instances when availability during container deploys matters.

Manual deploys refresh and load the app environment file, stop the prior app, and run Deploy commands as root in order on every instance. Any non-zero exit fails that instance and leaves its prior app stopped. Supervisord starts the Start command only after all preparation commands succeed. Manual mode does not automatically drain the ALB or run a health gate; implement those behaviors in your commands if required.

Maximum concurrent instances controls how many hosts update together, while Maximum deployment errors controls when the rollout stops sending commands to remaining hosts. Defaults are sequential and fail-fast. Per-instance deploy timeout limits each SSM script; the overall deployment has a 24-hour safety limit.

The EC2 deploy manager also runs the current release document against instances launched by autoscaling or replacement so they catch up to the active release.

### Logs and metrics

App stdout and stderr from the supervised process are sent to `/ravion/ec2/<service-name>` in CloudWatch Logs. Every deployment and instance has a distinct stream named `deployment/<deployment-id>/instance/<instance-id>`. SSM preparation and deploy-script stdout and stderr are copied into the same instance stream while remaining available with the deployment command.

The dashboard shows desired and in-service instance counts for every service. Web services also show request count, target response time, target 4xx and 5xx responses, and healthy and unhealthy host counts. Worker groups omit load-balancer metrics.

### Stable storage

| Storage         | Survives deploys | Survives instance termination |
| --------------- | ---------------- | ----------------------------- |
| Root volume     | Yes              | No                            |
| Data volume     | Yes              | No                            |
| EFS file system | Yes              | Yes                           |

Root and Data volumes are encrypted gp3 EBS volumes with delete-on-termination enabled. Use the Data volume only for independent, rebuildable per-host files such as caches, model downloads, or generated artifacts. Do not use it for the only copy of a database, upload, or job.

Enable EFS and select a Ravion EFS module for shared files that must outlive instances. The service mounts the file system through its access point when present and attaches the EFS client security group to each instance. Container mode bind-mounts the Data and EFS host paths into the app container at the same paths.

### Scaling and instance health

Minimum instances and Maximum instances bound the group. Enable CPU autoscaling to target average EC2 CPU utilization. When autoscaling is off, Desired instances sets the initial group size; later external scaling changes are preserved. Instance health check grace period controls how long new instances have before Auto Scaling begins health evaluation.

The Auto Scaling Group replaces instances that fail EC2 health checks. Load-balancer-based instance replacement is intentionally off by default because in-place deploys briefly deregister instances; override with Advanced Terraform variables (`health_check_type`) if you prefer it.

Launch template changes such as a new AMI, instance type, volume setting, or Additional user data apply to newly launched instances only. The module does not perform an instance refresh, so recycle existing instances deliberately when a host-level change must roll out.

### Application configuration

Runtime environment variables must be single-line values. They are rendered into the app environment file at instance boot and on every deploy. Container mode passes the file to Docker; Manual mode loads it before Deploy commands and the Start command. A configuration change requires a stack update and then a deploy to restart the app with the new values.

Runtime secrets use same-account Secrets Manager or SSM Parameter Store references. Each instance fetches values while rebuilding its environment file, so the values do not enter Terraform state or the SSM document. A rotated value takes effect on the next deploy without a stack change. Multi-line secret values are unsupported.

Build environment variables apply to Railpack and Dockerfile builds; for Dockerfile builds, optionally inject them as build arguments.

### Configuration

#### Service and instances

| Field                                     | Required | Default                     | Description                                                               |
| ----------------------------------------- | -------- | --------------------------- | ------------------------------------------------------------------------- |
| VPC network                               | Yes      | -                           | Existing network supplying account, region, VPC, and subnets              |
| Service name                              | Yes      | \{project}-\{env}-\{module} | Name for the instance group and related resources                         |
| Deploy type                               | Yes      | container                   | Switchable Container or Manual in-place deployment                        |
| Deploy commands                           | Yes\*    | -                           | Root-level, per-instance preparation commands for Manual mode             |
| Start command                             | Yes\*    | -                           | Foreground Manual-mode process managed by supervisord                     |
| Git repository                            | No       | -                           | Optional authenticated source checkout for Manual mode                    |
| Source base path                          | No       | .                           | Working directory within the selected repository                          |
| Maximum concurrent instances              | No       | 1                           | Instance count or percentage updated together                             |
| Maximum deployment errors                 | No       | 0                           | Failures tolerated before the rollout stops                               |
| Per-instance deploy timeout (secs)        | No       | 1200                        | SSM script timeout for either deploy mode                                 |
| Run in private subnets                    | No       | true                        | Use private subnets without public IPs; outbound access is still required |
| Instance type                             | Yes      | -                           | EC2 type used for every instance; architecture must match the app         |
| Minimum instances / Maximum instances     | Yes      | 1 / 3                       | Auto Scaling Group capacity bounds                                        |
| CPU autoscaling                           | No       | false                       | Target-tracking scaling on average EC2 CPU utilization                    |
| CPU target (%)                            | No       | 70                          | CPU target used when autoscaling is enabled                               |
| Desired instances                         | No       | -                           | Initial target when CPU autoscaling is off; blank starts at the minimum   |
| Instance health check grace period (secs) | No       | 300                         | Startup window before Auto Scaling health checks apply                    |
| SSH key pair name                         | No       | -                           | Optional SSH key; Session Manager does not require one                    |
| Custom AMI ID                             | No       | Latest AL2023               | AMI for future instances; must support the module bootstrap               |
| Additional security groups                | No       | \[]                         | Extra security groups attached to every instance                          |
| Additional user data                      | No       | -                           | Root shell script run after Ravion bootstrap on future instances          |

#### Container builds

| Field                                      | Required | Default           | Description                                                         |
| ------------------------------------------ | -------- | ----------------- | ------------------------------------------------------------------- |
| Build source                               | Yes\*    | dockerfile        | Dockerfile, Railpack, or Pull from image registry in Container mode |
| Git repository                             | Yes\*    | -                 | Source repository for Dockerfile and Railpack builds                |
| Source base path                           | No       | .                 | Repository-relative application and build root                      |
| Dockerfile path / build context            | No       | Dockerfile / .    | Dockerfile build locations relative to the source root              |
| Railpack version                           | No       | Ravion default    | Optional Railpack version override                                  |
| Railpack install / build / start commands  | No       | Detected          | Optional Railpack command overrides                                 |
| Image repository                           | Yes\*    | -                 | Repository without tag or digest for registry deployments           |
| Start command override                     | No       | Image CMD         | Container command override; preserves image ENTRYPOINT              |
| Build environment variables                | No       | \{}               | Values and secret references available only during image builds     |
| Inject environment variables in Dockerfile | No       | false             | Pass build environment values as Docker build arguments             |
| Builder instance type / size               | Yes\*    | ec2 / c7a.4xlarge | On-demand or Spot build runner and its EC2 size                     |
| Scan images on push                        | No       | true              | Run ECR basic vulnerability scanning for built images               |
| Force delete image repository              | No       | false             | Permit deletion of a non-empty Ravion-created ECR repository        |

Git branch and Git ref are optional inputs on each build run. They default to the repository's default branch and its current head.

#### Web routing and health

A web service can use a standalone Application Load Balancer or the public or private ALB from an ECS cluster. The selected load balancer must belong to the same AWS account, region, and VPC as the selected VPC network. When both HTTPS and HTTP listeners exist, the service attaches its listener rule to HTTPS.

| Field                                     | Required | Default                  | Description                                                              |
| ----------------------------------------- | -------- | ------------------------ | ------------------------------------------------------------------------ |
| Web service                               | No       | true                     | Attach HTTP routing to a shared ALB; disable for workers                 |
| Load balancer source                      | Yes\*    | Standalone ALB module    | Use a standalone ALB or an ALB from an ECS cluster                       |
| Standalone ALB module                     | Yes\*    | -                        | Standalone Ravion ALB receiving this service's listener rule             |
| ALB from ECS Cluster module               | Yes\*    | -                        | Cluster that supplies a public or private ALB                            |
| ALB to use from ECS Cluster module        | Yes\*    | Public                   | Select the cluster's public or private ALB                               |
| App port                                  | Yes\*    | 80                       | Host app port; Container mode maps the same container port and sets PORT |
| Domain host rules / Path rules            | No       | empty / empty            | Listener conditions; both empty produces `/*`                            |
| Listener rule priority                    | No       | AWS assigned             | Optional explicit ALB rule order                                         |
| Health check path                         | Yes\*    | /                        | Path used by the ALB and local container deploy gate                     |
| Success codes                             | Yes\*    | 200-399                  | ALB health matcher; local deploys separately require a status below 400  |
| Interval / Timeout (secs)                 | No       | 10 / 5                   | ALB health-check timing                                                  |
| Healthy / Unhealthy threshold             | No       | 2 / 2                    | Consecutive ALB checks needed to change health state                     |
| Slow start duration (secs)                | No       | 0                        | Gradually ramp traffic to new instances; 0 disables it                   |
| Deregistration delay (secs)               | No       | 30                       | Drain time when another target can serve traffic                         |
| Sticky sessions                           | No       | false                    | Keep repeat clients on one instance with an ALB or application cookie    |
| Stickiness type / application cookie name | Yes\*    | Load balancer cookie / - | Select cookie ownership and name application cookies                     |
| Stickiness cookie duration (secs)         | No       | 86400                    | Cookie lifetime when stickiness is enabled                               |
| Allowed CIDR blocks                       | No       | \[]                      | Direct app-port access that bypasses the load balancer                   |

#### Application data and operations

| Field                                  | Required | Default                              | Description                                                         |
| -------------------------------------- | -------- | ------------------------------------ | ------------------------------------------------------------------- |
| Runtime environment variables          | No       | \[]                                  | Plain single-line values refreshed on every deploy                  |
| Runtime secrets                        | No       | \[]                                  | Same-account Secrets Manager or SSM references fetched on each host |
| Root volume size (GB)                  | No       | 30                                   | Encrypted per-instance gp3 volume deleted with the instance         |
| Data volume / size / mount path        | No       | false / 20 / /data                   | Independent encrypted EBS volume for rebuildable per-host files     |
| EFS file system                        | No       | false                                | Enable shared storage that outlives instances                       |
| EFS file system reference / mount path | Yes\*    | - / /mnt/efs                         | Ravion EFS module and its host/container mount path                 |
| Log retention (days)                   | No       | 30                                   | CloudWatch retention for supervised app logs                        |
| Tags                                   | No       | Standard Ravion tags                 | Additional tags applied to resources                                |
| Advanced Terraform variables           | No       | \{}                                  | Lower-level overrides for exceptional settings                      |
| OpenTofu version override              | No       | Ravion default                       | Stack OpenTofu version override                                     |
| Ravion Terraform workspace name        | No       | \{project}-\{env}-\{module}-\{stack} | State backend workspace override                                    |

\*Conditionally required or visible based on Deploy type, Build source, Web service, Load balancer source, CPU autoscaling, Data volume, or EFS file system.

### Design decisions

* ECS is the default recommendation for stateless container services. Use this module when manual host deployment, host customization, or stable-host behavior is an actual requirement.
* Deploys are in place by design. They do not replace instances, but scaling, health recovery, and manual operations still can.
* Instances are prepared for both deploy modes so the Deploy type can change without replacing the group.
* Supervisord manages one app process per instance. A one-instance service is interrupted while that process is replaced.
* Supervisor 4.3.0 is installed from PyPI. Instances need outbound access to PyPI or an equivalent package source during bootstrap and when deploys ensure the pinned version.
* One app per instance group. Sharing instances between apps is intentionally out of scope; share the load balancer instead.
* Load balancing stays shared: use either a standalone ALB module or a public/private ALB from an ECS cluster while this service owns its target group and listener rule.
* The ASG health check type defaults to EC2 because in-place deploys briefly deregister instances from the target group.
* Launch template changes do not trigger an instance refresh, preserving per-host state until you choose to recycle instances.
* Worker groups omit load-balancer metrics instead of rendering charts with empty target-group dimensions.

### Learn more

* [Amazon EC2 Auto Scaling](https://docs.aws.amazon.com/autoscaling/ec2/userguide/what-is-amazon-ec2-auto-scaling.html)
* [AWS Systems Manager Run Command](https://docs.aws.amazon.com/systems-manager/latest/userguide/run-command.html)
* [Application Load Balancer listener rules](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/listener-update-rules.html)
* [Amazon ECS services](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs_services.html)
* [Supervisor documentation](https://supervisord.org/)
* [Railpack documentation](https://railpack.com/)

## Inputs reference

All inputs for `rvn-ec2-service` version `0.1.1`. Use the `name` shown for each field as the input key in module config.

<ResponseField name="network" type="$ref:rvn-aws-network" required>
  **VPC network.** Existing Ravion network that supplies the AWS account, region, VPC, and public and private subnets for the instances.

  * Immutable after creation
</ResponseField>

<ResponseField name="private_subnet_placement_enabled" type="boolean">
  **Run in private subnets.** Recommended. Requires a NAT gateway or equivalent for internet access and a static IP.

  * Default: `true`
</ResponseField>

### EC2 service config

<ResponseField name="name" type="string" required>
  **Service name.** Name for the instance group and related resources.

  * Default: `<<project.given_id>>-<<environment.given_id>>-<<module.given_id>>`
  * Immutable after creation
  * Pattern: `^[a-z0-9]([a-z0-9-]{0,26}[a-z0-9])?$` — 1-28 lowercase letters, numbers, and hyphens. Start and end with a letter or number.
</ResponseField>

<ResponseField name="instance_type" type="string" required>
  **Instance type.**
</ResponseField>

<ResponseField name="ami_id" type="string">
  **Custom AMI ID.** Custom AMI for new instances. Leave blank for the latest architecture-matched Amazon Linux 2023 AMI.
</ResponseField>

<ResponseField name="key_name" type="string">
  **SSH key pair name.** Optional EC2 key pair for SSH access.
</ResponseField>

<ResponseField name="additional_user_data" type="text">
  **Additional user data.** Shell script run as root after Ravion's bootstrap on every newly launched instance.
</ResponseField>

<ResponseField name="http_traffic_enabled" type="boolean">
  **Serve HTTP traffic.** Off for a worker. On to serve HTTP traffic through a load balancer.

  * Default: `true`
</ResponseField>

<ResponseField name="deploy_type" type="string" required>
  **Deploy type.** Choose in-place container image deploys or host-level shell commands.

  * Default: `container`
  * Allowed values: `container` (Container), `manual` (Manual)
</ResponseField>

### Build config

<ResponseField name="build_source" type="string" required>
  **Build source.** Build a container image from source with Dockerfile or Railpack, or deploy an existing image from a configured registry repository.

  * Default: `dockerfile`
  * Allowed values: `dockerfile` (Dockerfile), `railpack` (Railpack), `image_registry` (Pull from image registry)
  * Shown when: `{"deploy_type":"container"}`
</ResponseField>

<ResponseField name="source_repo" type="gitrepo" required>
  **Git repository.** Repository containing the application source for Dockerfile or Railpack builds.

  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

<ResponseField name="source_base_path" type="string">
  **Source base path.** Repository-relative source and build root.

  * Default: `.`
  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

<ResponseField name="image_repository" type="string" required>
  **Image repository.** Repository without a tag or digest, such as `nginx` or `123456789012.dkr.ecr.us-east-1.amazonaws.com/app`. Registries requiring Docker credentials are unsupported. Same-region private ECR also needs a repository policy that lets the service instance role pull images.

  * Shown when: `{"build_source":"image_registry","deploy_type":"container"}`
</ResponseField>

<ResponseField name="container_start_command" type="string">
  **Start command override.** Optional command string that overrides the image CMD. The image ENTRYPOINT is preserved.

  * Shown when: `{"deploy_type":"container"}`
</ResponseField>

### Docker

<ResponseField name="dockerfile" type="string">
  **Dockerfile path.** Path to the Dockerfile to use for the build, relative to the repository root or configured source base path.

  * Shown when: `{"build_source":"dockerfile","deploy_type":"container"}`
</ResponseField>

<ResponseField name="dockerfile_context" type="string">
  **Docker build context path.** Directory to use as the Docker build context, relative to the repository root or configured source base path.

  * Shown when: `{"build_source":"dockerfile","deploy_type":"container"}`
</ResponseField>

### Railpack

<ResponseField name="railpack_version" type="string">
  **Railpack version.** Optional Railpack version to use for the build. Leave blank to use the Ravion default.

  * Pattern: `^(|latest|v?[0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?)$` — Leave blank, use latest, a semantic version like 0.29.0, or a v-prefixed version like v0.29.0.
  * Shown when: `{"build_source":"railpack","deploy_type":"container"}`
</ResponseField>

<ResponseField name="railpack_install_cmd" type="string">
  **Install command.** Optional dependency installation command. Leave blank to use Railpack detection.

  * Shown when: `{"build_source":"railpack","deploy_type":"container"}`
</ResponseField>

<ResponseField name="railpack_build_cmd" type="string">
  **Build command.** Optional application build command. Leave blank to use Railpack detection.

  * Shown when: `{"build_source":"railpack","deploy_type":"container"}`
</ResponseField>

<ResponseField name="railpack_start_cmd" type="string">
  **Start command.** Optional start command embedded in the Railpack-built image. Leave blank to use Railpack detection.

  * Shown when: `{"build_source":"railpack","deploy_type":"container"}`
</ResponseField>

### Deployment

<ResponseField name="deploy_source_repo" type="gitrepo">
  **Git repository.** Optional repository to check out before manual deploy commands run. Leave blank to run the commands without a managed source checkout.

  * Shown when: `{"deploy_type":"manual"}`
</ResponseField>

<ResponseField name="deploy_source_base_path" type="string">
  **Source base path.** Repository-relative working directory for manual deploy and start commands when a Git repository is selected.

  * Default: `.`
  * Shown when: `{"deploy_source_repo":{"not":""},"deploy_type":"manual"}`
</ResponseField>

<ResponseField name="deploy_commands" type="string_array" required>
  **Deploy commands.** Release preparation commands run as root, in order, on every instance during each manual deploy. Keep them idempotent; any command that exits non-zero fails that instance's deploy.

  * Shown when: `{"deploy_type":"manual"}`
</ResponseField>

<ResponseField name="start_command" type="string" required>
  **Start command.** Long-running foreground app command that supervisord runs as root after preparation and restarts if it exits. Use a wrapper to drop privileges when needed; the command must not daemonize.

  * Shown when: `{"deploy_type":"manual"}`
</ResponseField>

<ResponseField name="deployment_concurrency_max" type="string" required>
  **Maximum concurrent instances.** Maximum instances updated at once. Use an instance count such as 1 or a percentage such as 25%.

  * Default: `1`
  * Pattern: `^([1-9][0-9]*|[1-9][0-9]?%|100%)$` — Positive instance count or percentage from 1% to 100%.
</ResponseField>

<ResponseField name="deployment_errors_max" type="string" required>
  **Maximum deployment errors.** Number or percentage of failed instances tolerated before the deployment stops on remaining instances.

  * Default: `0`
  * Pattern: `^(0|[1-9][0-9]*|[1-9][0-9]?%|100%)$` — Zero, a positive error count, or percentage from 1% to 100%.
</ResponseField>

<ResponseField name="deploy_timeout_seconds" type="number">
  **Per-instance deploy timeout (secs).** Maximum time allowed for the SSM deploy script on each instance, for both container and manual deploys.

  * Default: `1200`
  * Min: `60`
  * Max: `14400`
</ResponseField>

### HTTP routing

<ResponseField name="load_balancer_source" type="string" required>
  **Load balancer source.** Must use the same AWS account, region, and VPC as the VPC network.

  * Default: `standalone_alb`
  * Allowed values: `standalone_alb` (Standalone ALB module), `ecs_cluster` (ALB from ECS Cluster module)
  * Immutable after creation
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="alb" type="$ref:rvn-aws-alb" required>
  **Standalone ALB module.** Attach listener rules to this.

  * Immutable after creation
  * Shown when: `{"http_traffic_enabled":true,"load_balancer_source":"standalone_alb"}`
</ResponseField>

<ResponseField name="ecs_cluster" type="$ref:rvn-ecs-cluster" required>
  **ECS Cluster.** Attach listener rules to the ALB in this cluster.

  * Immutable after creation
  * Shown when: `{"http_traffic_enabled":true,"load_balancer_source":"ecs_cluster"}`
</ResponseField>

<ResponseField name="ecs_cluster_alb_visibility" type="string" required>
  **ALB to use from ECS Cluster module.** The selected load balancer must be enabled on the cluster.

  * Default: `public`
  * Allowed values: `public` (Public), `private` (Private)
  * Shown when: `{"http_traffic_enabled":true,"load_balancer_source":"ecs_cluster"}`
</ResponseField>

<ResponseField name="app_port" type="number" required>
  **App port.** Port the host application listens on. Container mode publishes the same container port, and PORT is set automatically.

  * Default: `80`
  * Min: `1`
  * Max: `65535`
  * Immutable after creation
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

### HTTP listener rules

<ResponseField name="host_header_values" type="string_array">
  **Domain host rules.** Hostnames that should route to this service, such as app.example.com or \*.example.com. Leave empty to use path-based routing.

  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="path_pattern_values" type="string_array">
  **Path rules.** Path patterns that should route to this service, such as /*, /api/*, or /app/*. If both domain host rules and path rules are empty, the service routes all paths with /*.

  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="listener_rule_priority" type="number">
  **Listener rule priority.** Optional ALB listener rule priority. Leave blank to let AWS assign the next available priority.

  * Min: `1`
  * Max: `50000`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

### Health check

<ResponseField name="health_check_path" type="string" required>
  **Health check path.** HTTP path used by the load balancer health check and by the Container-mode deploy gate on each instance.

  * Default: `/`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="health_check_matcher" type="string" required>
  **Success codes.** HTTP status codes the ALB treats as healthy, such as 200-399.

  * Default: `200-399`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="health_check_interval" type="number">
  **Interval (secs).** Seconds between Application Load Balancer health checks.

  * Default: `10`
  * Min: `5`
  * Max: `300`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="health_check_timeout" type="number">
  **Timeout (secs).** Seconds the Application Load Balancer waits for a health-check response. Must be lower than the interval.

  * Default: `5`
  * Min: `2`
  * Max: `120`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="healthy_threshold" type="number">
  **Healthy threshold.** Consecutive successful ALB checks required before an instance is healthy.

  * Default: `2`
  * Min: `2`
  * Max: `10`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="unhealthy_threshold" type="number">
  **Unhealthy threshold.** Consecutive failed ALB checks required before an instance is unhealthy.

  * Default: `2`
  * Min: `2`
  * Max: `10`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="health_check_grace_period" type="number">
  **Instance health check grace period (secs).** Seconds after launch before Auto Scaling health checks apply.

  * Default: `300`
  * Min: `0`
</ResponseField>

<ResponseField name="target_group_slow_start" type="number">
  **Slow start duration (secs).** Seconds the load balancer gradually increases traffic to a newly healthy instance. Use 0 to disable.

  * Default: `0`
  * Min: `0`
  * Max: `900`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="deregistration_delay" type="number">
  **Deregistration delay (secs).** Seconds allowed for in-flight requests to drain before a Container-mode in-place swap.

  * Default: `30`
  * Min: `0`
  * Max: `3600`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="target_group_stickiness_enabled" type="boolean">
  **Sticky sessions.** Keep repeat requests on the same instance using a load balancer or application cookie.

  * Default: `false`
  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

<ResponseField name="target_group_stickiness_type" type="string" required>
  **Stickiness type.** Use a load-balancer-generated cookie or an application cookie.

  * Default: `lb_cookie`
  * Allowed values: `lb_cookie` (Load balancer cookie), `app_cookie` (Application cookie)
  * Shown when: `{"http_traffic_enabled":true,"target_group_stickiness_enabled":true}`
</ResponseField>

<ResponseField name="target_group_stickiness_cookie_name" type="string" required>
  **Application cookie name.** Application cookie name used for target stickiness.

  * Shown when: `{"http_traffic_enabled":true,"target_group_stickiness_enabled":true,"target_group_stickiness_type":"app_cookie"}`
</ResponseField>

<ResponseField name="target_group_stickiness_cookie_duration" type="number">
  **Stickiness cookie duration (secs).** How long the cookie keeps a client routed to the same instance.

  * Default: `86400`
  * Min: `1`
  * Max: `604800`
  * Shown when: `{"http_traffic_enabled":true,"target_group_stickiness_enabled":true}`
</ResponseField>

### Storage

<ResponseField name="root_volume_size" type="number">
  **Root volume size (GB).** Required encrypted boot disk for the operating system, Docker, and local files. It is deleted when the instance is terminated.

  * Default: `30`
  * Min: `8`
  * Max: `16384`
</ResponseField>

<ResponseField name="data_volume_creation_enabled" type="boolean">
  **Data volume.** Attach a second encrypted EBS disk for per-instance files such as caches or generated artifacts. Each instance gets its own disk, which is deleted with that instance.

  * Default: `false`
</ResponseField>

<ResponseField name="data_volume_size" type="number">
  **Data volume size (GB).** Size of the dedicated EBS data volume attached to each instance.

  * Default: `20`
  * Min: `1`
  * Max: `16384`
  * Shown when: `{"data_volume_creation_enabled":true}`
</ResponseField>

<ResponseField name="data_volume_mount_path" type="string" required>
  **Data volume mount path.** Absolute host path for the data volume.

  * Default: `/data`
  * Pattern: `^/` — Use an absolute path.
  * Shown when: `{"data_volume_creation_enabled":true}`
</ResponseField>

<ResponseField name="efs_enabled" type="boolean">
  **EFS file system.** Mount a shared network filesystem that every instance can access. Files survive instance replacement and remain available at the configured mount path.

  * Default: `false`
</ResponseField>

<ResponseField name="efs" type="$ref:rvn-efs" required>
  **EFS file system.**

  * Shown when: `{"efs_enabled":true}`
</ResponseField>

<ResponseField name="efs_mount_path" type="string" required>
  **EFS mount path.** Host path where the file system is mounted.

  * Default: `/mnt/efs`
  * Pattern: `^/` — Use an absolute path.
  * Shown when: `{"efs_enabled":true}`
</ResponseField>

### Scaling

<ResponseField name="min_capacity" type="number" required>
  **Minimum instances.**

  * Default: `1`
  * Min: `0`
</ResponseField>

<ResponseField name="max_capacity" type="number" required>
  **Maximum instances.**

  * Default: `3`
  * Min: `1`
</ResponseField>

<ResponseField name="cpu_autoscaling_enabled" type="boolean">
  **CPU autoscaling.**

  * Default: `false`
</ResponseField>

<ResponseField name="cpu_target_value" type="number">
  **CPU target (%).** Average EC2 CPU utilization that target-tracking autoscaling tries to maintain.

  * Default: `70`
  * Min: `1`
  * Max: `100`
  * Shown when: `{"cpu_autoscaling_enabled":true}`
</ResponseField>

<ResponseField name="desired_capacity" type="number">
  **Desired instances.** Initial group size when CPU autoscaling is off. Leave blank to start at the minimum.

  * Min: `0`
  * Shown when: `{"cpu_autoscaling_enabled":false}`
</ResponseField>

### Environment variables

<ResponseField name="build_environment_variables" type="object">
  **Build environment variables.** Environment variables available during builds. Values can be plain strings or references loaded from Parameter Store or Secrets Manager.

  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

<ResponseField name="dockerfile_inject_env_variables" type="boolean">
  **Inject environment variables in Dockerfile.** Pass build environment variables into Dockerfile builds as build arguments.

  * Default: `false`
  * Shown when: `{"build_source":"dockerfile","deploy_type":"container"}`
</ResponseField>

<ResponseField name="environment_variables" type="array">
  **Runtime environment variables.** Plain, single-line environment variables written to each instance at boot and refreshed on every deploy.
</ResponseField>

<ResponseField name="secrets" type="array">
  **Runtime secrets.** Secret environment variables as \{name, value\_from} objects fetched on each instance.
</ResponseField>

### Networking

<ResponseField name="additional_security_group_ids" type="string_array">
  **Additional security groups.** Additional security groups attached to the instances.
</ResponseField>

<ResponseField name="direct_access_cidr_blocks" type="string_array">
  **Allowed CIDR blocks.** IPv4 CIDR blocks allowed to reach the app port directly, bypassing the load balancer.

  * Shown when: `{"http_traffic_enabled":true}`
</ResponseField>

### Builder config

<ResponseField name="build_infrastructure_type" type="string" required>
  **Builder instance type.** Use on-demand EC2 for predictable availability or EC2 Spot for lower cost with possible capacity delays or interruption.

  * Default: `ec2`
  * Allowed values: `ec2` (EC2), `ec2-spot` (EC2 spot)
  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

<ResponseField name="build_instance_size" type="string" required>
  **Builder instance size.** EC2 instance type for builds. Start with the default value, then increase or decrease it based on the resource usage report at the end of builds.

  * Default: `c7a.4xlarge`
  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

<ResponseField name="build_execution_environment_id" type="string">
  **Builder execution environment.** Optional execution environment ID or given ID for builds. Defaults to the module Terraform execution environment.

  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

<ResponseField name="build_ami" type="string">
  **Builder AMI.** Optional AMI ID for build runners. Leave empty to use the default runner image.

  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

### Container registry

<ResponseField name="ecr_scan_on_push_enabled" type="boolean">
  **Scan images on push.** Scan images for vulnerabilities after they are pushed to the Ravion-created ECR repository.

  * Default: `true`
  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

<ResponseField name="ecr_force_deletion_enabled" type="boolean">
  **Force delete image repository.** Allow the Ravion-created ECR repository to be deleted while it still contains images.

  * Default: `false`
  * Shown when: `{"build_source":["dockerfile","railpack"],"deploy_type":"container"}`
</ResponseField>

### Logging

<ResponseField name="log_retention_in_days" type="number">
  **Log retention (days).** CloudWatch retention for app logs.

  * Default: `30`
  * Min: `1`
</ResponseField>

<ResponseField name="tags" type="keyvalue">
  **Tags.** A map of tags to assign to all resources. Default tags are `Owner`, `ProjectGivenId`, `EnvironmentGivenId`, `ModuleGivenId`, `ModuleId`
</ResponseField>

### Terraform settings

<ResponseField name="opentofu_version" type="string">
  **OpenTofu version override.** Override the environment's default version for this module
</ResponseField>

<ResponseField name="ravion_state_backend_workspace" type="string">
  **Ravion Terraform workspace name.** Override Terraform state backend workspace name. Defaults to project + environment + module given ids.

  * Immutable after creation
</ResponseField>

<ResponseField name="advanced_terraform_variables" type="object">
  **Advanced Terraform variables.** Optional raw Terraform variable overrides for advanced module inputs or one-off overrides. Values here override the generated variables above.

  * Default: `{}`
</ResponseField>
