rvn-ec2-service · Latest version: 0.1.1
Dependencies and consumers
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_serviceUse cases
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:
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
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 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 anapp user and an initial checkout with Additional user data, then configure:
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 port3000, 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:- Rebuilds the app environment file and fetches configured secret values on the instance.
- Pulls the selected image before interrupting the running app.
- For a web service with another registered target, deregisters and drains this instance.
- Stops the prior process, replaces the app container, and starts it under supervisord.
- Polls the local health path for an HTTP response below 400, failing that instance’s deploy otherwise.
- Re-registers a drained instance and waits for the ALB to mark it in service.
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
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
Container builds
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.Application data and operations
*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
- AWS Systems Manager Run Command
- Application Load Balancer listener rules
- Amazon ECS services
- Supervisor documentation
- Railpack documentation
Inputs reference
All inputs forrvn-ec2-service version 0.1.1. Use the name shown for each field as the input key in module config.
$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
boolean
Run in private subnets. Recommended. Requires a NAT gateway or equivalent for internet access and a static IP.
- Default:
true
EC2 service config
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.
string
required
Instance type.
string
Custom AMI ID. Custom AMI for new instances. Leave blank for the latest architecture-matched Amazon Linux 2023 AMI.
string
SSH key pair name. Optional EC2 key pair for SSH access.
text
Additional user data. Shell script run as root after Ravion’s bootstrap on every newly launched instance.
boolean
Serve HTTP traffic. Off for a worker. On to serve HTTP traffic through a load balancer.
- Default:
true
string
required
Deploy type. Choose in-place container image deploys or host-level shell commands.
- Default:
container - Allowed values:
container(Container),manual(Manual)
Build config
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"}
gitrepo
required
Git repository. Repository containing the application source for Dockerfile or Railpack builds.
- Shown when:
{"build_source":["dockerfile","railpack"],"deploy_type":"container"}
string
Source base path. Repository-relative source and build root.
- Default:
. - Shown when:
{"build_source":["dockerfile","railpack"],"deploy_type":"container"}
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"}
string
Start command override. Optional command string that overrides the image CMD. The image ENTRYPOINT is preserved.
- Shown when:
{"deploy_type":"container"}
Docker
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"}
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"}
Railpack
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"}
string
Install command. Optional dependency installation command. Leave blank to use Railpack detection.
- Shown when:
{"build_source":"railpack","deploy_type":"container"}
string
Build command. Optional application build command. Leave blank to use Railpack detection.
- Shown when:
{"build_source":"railpack","deploy_type":"container"}
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"}
Deployment
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"}
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"}
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"}
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"}
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%.
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%.
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
HTTP routing
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}
$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"}
$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"}
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"}
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}
HTTP listener rules
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}
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}
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}
Health check
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}
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}
number
Interval (secs). Seconds between Application Load Balancer health checks.
- Default:
10 - Min:
5 - Max:
300 - Shown when:
{"http_traffic_enabled":true}
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}
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}
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}
number
Instance health check grace period (secs). Seconds after launch before Auto Scaling health checks apply.
- Default:
300 - Min:
0
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}
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}
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}
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}
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"}
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}
Storage
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
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
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}
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}
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
$ref:rvn-efs
required
EFS file system.
- Shown when:
{"efs_enabled":true}
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}
Scaling
number
required
Minimum instances.
- Default:
1 - Min:
0
number
required
Maximum instances.
- Default:
3 - Min:
1
boolean
CPU autoscaling.
- Default:
false
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}
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}
Environment variables
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"}
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"}
array
Runtime environment variables. Plain, single-line environment variables written to each instance at boot and refreshed on every deploy.
array
Runtime secrets. Secret environment variables as {name, value_from} objects fetched on each instance.
Networking
string_array
Additional security groups. Additional security groups attached to the instances.
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}
Builder config
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"}
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"}
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"}
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"}
Container registry
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"}
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"}
Logging
number
Log retention (days). CloudWatch retention for app logs.
- Default:
30 - Min:
1
keyvalue
Tags. A map of tags to assign to all resources. Default tags are
Owner, ProjectGivenId, EnvironmentGivenId, ModuleGivenId, ModuleIdTerraform settings
string
OpenTofu version override. Override the environment’s default version for this module
string
Ravion Terraform workspace name. Override Terraform state backend workspace name. Defaults to project + environment + module given ids.
- Immutable after creation
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:
{}