Co-authored-by: Aaron D. Lee <himself@adlee.work> Co-committed-by: Aaron D. Lee <himself@adlee.work>
5.1 KiB
AWS appendix — ALB + ACM (docs only, never a shipped artifact)
Scope: operators already on EC2. There, an ALB is trust-neutral (the engine is on AWS infrastructure regardless) and ACM renewal is genuinely zero-touch. This page is an appendix, not a recommendation to move to AWS — the shipped edges are Caddy (topologies.md) and BYO proxies (reverse-proxies.md). All ALB/NLB facts below are grounded in the TLS/edge decision brief §3(c).
Never NLB-TLS
Do not put the engine behind an NLB TLS listener. Two disqualifiers, ratified in ADR-0011:
- Fixed 350-second idle timeout that silently drops flows — any quiet WebSocket (hold, parked call, unidirectional stream) dies mid-call with no close frame.
- No HTTP context: an NLB adds no
X-Forwarded-Proto/Host, so the engine cannot reconstruct the public URL thatX-Twilio-Signatureis HMAC'd over (Twilio's SSL-termination guidance) — genuine traffic fails validation.
The three mandatory ALB attribute overrides
The defaults kill telephony. All three overrides are mandatory, not tuning:
| Attribute | Value | Why |
|---|---|---|
idle_timeout.timeout_seconds |
4000 |
The default 60 s kills any quiet WS. 4000 is the ALB maximum — still finite, so the engine's app-level WS pings (RUTSTER_WS_PING_SECS, default 20) remain load-bearing here. |
client_keep_alive.seconds |
604800 |
The 3600 s default can terminate the client connection under a >1-hour call. Set to the 7-day maximum. |
routing.http.preserve_host_header.enabled |
true |
Signature validation reconstructs the URL as Twilio saw it — the engine must receive the honest public Host, and its RUTSTER_TRUSTED_PROXIES must include the ALB's subnet CIDRs. |
Copy-paste path (CLI)
Assumes: engine node(s) running per quickstart-docker.md with the
caddy service disabled (plaintext :8080), a VPC, two subnets, and a security group
allowing :443 from anywhere and :8080 from the ALB.
VPC=vpc-0123456789abcdef0
SUBNETS="subnet-0aaa subnet-0bbb"
SG=sg-0ccc
ALB_ARN=$(aws elbv2 create-load-balancer --name rutster-edge --type application \
--subnets $SUBNETS --security-groups $SG \
--query 'LoadBalancers[0].LoadBalancerArn' --output text)
# THE THREE MANDATORY OVERRIDES — do not skip:
aws elbv2 modify-load-balancer-attributes --load-balancer-arn "$ALB_ARN" --attributes \
Key=idle_timeout.timeout_seconds,Value=4000 \
Key=client_keep_alive.seconds,Value=604800 \
Key=routing.http.preserve_host_header.enabled,Value=true
TG_ARN=$(aws elbv2 create-target-group --name rutster-node-1 \
--protocol HTTP --port 8080 --vpc-id "$VPC" \
--health-check-path /readyz \
--query 'TargetGroups[0].TargetGroupArn' --output text)
aws elbv2 register-targets --target-group-arn "$TG_ARN" --targets Id=i-0node1
CERT_ARN=$(aws acm request-certificate --domain-name '*.pbx.example.com' \
--validation-method DNS --query CertificateArn --output text)
# Create the DNS validation CNAME ACM reports, wait for ISSUED, then:
LISTENER_ARN=$(aws elbv2 create-listener --load-balancer-arn "$ALB_ARN" \
--protocol HTTPS --port 443 \
--ssl-policy ELBSecurityPolicy-TLS13-1-2-2021-06 \
--certificates CertificateArn="$CERT_ARN" \
--default-actions Type=forward,TargetGroupArn="$TG_ARN" \
--query 'Listeners[0].ListenerArn' --output text)
# Fleet growth: one target group + one host-header rule per node
# (node-addressed placement — ADR-0011):
aws elbv2 create-rule --listener-arn "$LISTENER_ARN" --priority 10 \
--conditions Field=host-header,Values=node-1.pbx.example.com \
--actions Type=forward,TargetGroupArn="$TG_ARN"
Point *.pbx.example.com (alias record) at the ALB. First call: identical to
quickstart-docker.md — browser at your domain, Twilio webhook at
https://pbx.example.com/v1/trunk/webhook.
The --ssl-policy above serves TLS 1.2+1.3 — never pick a 1.3-only policy (Twilio's TLS 1.3
client support is undocumented; see the invariants in
certificates.md).
WebRTC media does not ride the ALB
The ALB carries HTTPS/WSS only. WebRTC media UDP goes direct to each node: give every
engine node an Elastic IP, open the media UDP range in the node's security group, and set
RUTSTER_MEDIA_ADVERTISED_IP to that EIP.
Caps and traps
- Hard cap: 100 target groups per ALB. With one TG per node (host-header routing), that is your fleet ceiling per ALB.
- AWS fleet rotation can still cut a multi-hour call — ALB infrastructure is replaced under maintenance without regard for your WS lifetimes. Accepted residual risk; there is no attribute for it.
- Health checks target
/readyz(503 while draining or at the admission cap) — scale-in must be drain-then-terminate: deregister the target, wait outRUTSTER_DRAIN_DEADLINE_SECS, then terminate the instance.