AWS ECS Service Connect in 2026: Service-to-Service Communication Without Service Discovery
Use AWS ECS Service Connect for microservice communication: no service discovery setup, built-in load balancing, circuit breaking, Terraform configuration, and observability with CloudWatch.
AWS ECS Service Connect in 2026: Service-to-Service Communication Without Service Discovery
Before ECS Service Connect, connecting microservices in ECS required: Cloud Map namespaces, service discovery configurations, DNS TTL headaches, and custom health check logic. Service Connect, GA since late 2022 and mature in 2026, replaces all of that with a sidecar proxy that handles service-to-service routing automatically.
Services communicate using simple DNS names within the cluster (http://api-service:3000), Service Connect handles load balancing across task instances, and you get built-in circuit breaking and connection retries—without deploying a full service mesh like App Mesh.
Service Connect vs Alternatives
| Feature | Service Connect | Cloud Map + DNS | App Mesh | ALB |
|---|---|---|---|---|
| Setup complexity | Low | Medium | High | Low |
| Service-to-service routing | ✅ | ✅ | ✅ | ❌ (external only) |
| Circuit breaking | ✅ Built-in | ❌ | ✅ | ❌ |
| mTLS | ❌ | ❌ | ✅ | ❌ |
| Observability | CloudWatch | Basic | X-Ray + CW | ALB logs |
| Cost | Sidecar overhead | Cloud Map API calls | Complex pricing | ALB hours |
| Best for | Simple–medium microservices | Legacy ECS setups | Enterprise mTLS | Public endpoints |
Use Service Connect when you have 2–10 internal services that need to call each other and you don't need mTLS. Upgrade to App Mesh only when you need mutual TLS or advanced traffic shaping.
Architecture
ECS Cluster (with Service Connect namespace)
├── api-service (port 3000)
│ └── Service Connect proxy sidecar (transparent)
├── auth-service (port 4000)
│ └── Service Connect proxy sidecar
├── notification-service (port 5000)
│ └── Service Connect proxy sidecar
└── worker-service (no ingress port needed)
# api-service calls auth-service:
fetch("http://auth-service:4000/validate")
# → intercepted by local proxy
# → routed to a healthy auth-service task instance
# → circuit breaker monitors failures
☁️ Is Your Cloud Costing Too Much?
Most teams overspend 30–40% on cloud — wrong instance types, no reserved pricing, bloated storage. We audit, right-size, and automate your infrastructure.
- AWS, GCP, Azure certified engineers
- Infrastructure as Code (Terraform, CDK)
- Docker, Kubernetes, GitHub Actions CI/CD
- Typical audit recovers $500–$3,000/month in savings
Terraform Configuration
# terraform/ecs-service-connect.tf
# 1. Create the Service Connect namespace
resource "aws_service_discovery_http_namespace" "main" {
name = "${var.name}-${var.environment}"
description = "Service Connect namespace for ${var.name} ${var.environment}"
tags = var.common_tags
}
# 2. ECS Cluster — enable Service Connect defaults
resource "aws_ecs_cluster" "main" {
name = "${var.name}-${var.environment}"
service_connect_defaults {
namespace = aws_service_discovery_http_namespace.main.arn
}
setting {
name = "containerInsights"
value = "enabled"
}
tags = var.common_tags
}
# 3. API Service — with Service Connect client + server config
resource "aws_ecs_service" "api" {
name = "${var.name}-${var.environment}-api"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.api.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnet_ids
security_groups = [aws_security_group.ecs_tasks.id]
assign_public_ip = false
}
service_connect_configuration {
enabled = true
namespace = aws_service_discovery_http_namespace.main.arn
# This service IS discoverable by other services as "api-service"
service {
port_name = "api" # Must match container port_mappings name
discovery_name = "api-service" # DNS name other services use
client_alias {
port = 3000
dns_name = "api-service" # http://api-service:3000
}
}
# Logging for the Service Connect proxy sidecar
log_configuration {
log_driver = "awslogs"
options = {
awslogs-group = "/ecs/${var.name}-${var.environment}/service-connect"
awslogs-region = var.aws_region
awslogs-stream-prefix = "api"
}
}
}
load_balancer {
target_group_arn = aws_lb_target_group.api.arn
container_name = "api"
container_port = 3000
}
lifecycle {
ignore_changes = [desired_count]
}
}
# 4. Auth Service — internal only (no ALB)
resource "aws_ecs_service" "auth" {
name = "${var.name}-${var.environment}-auth"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.auth.arn
desired_count = 2
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnet_ids
security_groups = [aws_security_group.ecs_tasks.id]
assign_public_ip = false
}
service_connect_configuration {
enabled = true
namespace = aws_service_discovery_http_namespace.main.arn
service {
port_name = "auth"
discovery_name = "auth-service"
client_alias {
port = 4000
dns_name = "auth-service"
}
}
}
# No load_balancer block — internal service only
}
# 5. Worker Service — client only (calls other services but isn't called)
resource "aws_ecs_service" "worker" {
name = "${var.name}-${var.environment}-worker"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.worker.arn
desired_count = 1
launch_type = "FARGATE"
network_configuration {
subnets = var.private_subnet_ids
security_groups = [aws_security_group.ecs_tasks.id]
assign_public_ip = false
}
service_connect_configuration {
enabled = true
namespace = aws_service_discovery_http_namespace.main.arn
# No `service` block — this service is a client only, not a server
}
}
Task Definition with Named Port Mappings
# terraform/task-definitions.tf
resource "aws_ecs_task_definition" "api" {
family = "${var.name}-${var.environment}-api"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = 512
memory = 1024
execution_role_arn = aws_iam_role.ecs_execution.arn
task_role_arn = aws_iam_role.ecs_task.arn
container_definitions = jsonencode([
{
name = "api"
image = "${aws_ecr_repository.api.repository_url}:${var.image_tag}"
essential = true
portMappings = [
{
name = "api" # MUST match service_connect_configuration service.port_name
containerPort = 3000
hostPort = 3000
protocol = "tcp"
appProtocol = "http" # Enables HTTP/2 in Service Connect proxy
}
]
environment = [
{ name = "NODE_ENV", value = var.environment },
# Service Connect makes these DNS names work automatically:
{ name = "AUTH_SERVICE_URL", value = "http://auth-service:4000" },
{ name = "NOTIF_SERVICE_URL", value = "http://notification-service:5000" },
]
logConfiguration = {
logDriver = "awslogs"
options = {
awslogs-group = "/ecs/${var.name}-${var.environment}/api"
awslogs-region = var.aws_region
awslogs-stream-prefix = "api"
}
}
}
])
}
⚙️ DevOps Done Right — Zero Downtime, Full Automation
Ship faster without breaking things. We build CI/CD pipelines, monitoring stacks, and auto-scaling infrastructure that your team can actually maintain.
- Staging + production environments with feature flags
- Automated security scanning in the pipeline
- Uptime monitoring + alerting + runbook automation
- On-call support handover docs included
Service-to-Service Communication in Application Code
// lib/services/auth-client.ts
// Calls auth-service via Service Connect DNS — no SDK or discovery needed
interface ValidateTokenResponse {
valid: boolean;
userId?: string;
teamId?: string;
role?: string;
}
const AUTH_SERVICE_URL = process.env.AUTH_SERVICE_URL ?? "http://auth-service:4000";
export async function validateToken(token: string): Promise<ValidateTokenResponse> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000); // 5s timeout
try {
const response = await fetch(`${AUTH_SERVICE_URL}/validate`, {
method: "POST",
headers: {
"Content-Type": "application/json",
// Internal service auth header (shared secret or JWT)
"X-Internal-Key": process.env.INTERNAL_SERVICE_KEY!,
},
body: JSON.stringify({ token }),
signal: controller.signal,
});
if (!response.ok) {
if (response.status === 401) {
return { valid: false };
}
throw new Error(`Auth service error: ${response.status}`);
}
return response.json();
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
throw new Error("Auth service timeout");
}
throw err;
} finally {
clearTimeout(timeout);
}
}
// The Service Connect proxy handles:
// - Load balancing across auth-service task instances
// - Health checks (unhealthy tasks stop receiving traffic)
// - Connection retries (configurable)
// - Circuit breaking (configurable threshold)
// - Metrics emission to CloudWatch
Observability
Service Connect automatically emits metrics to CloudWatch under the AWS/ECS/ManagedScaling namespace. Key metrics to monitor:
# CloudWatch alarms for Service Connect health
resource "aws_cloudwatch_metric_alarm" "auth_service_5xx" {
alarm_name = "${var.name}-auth-service-5xx-rate"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "HTTPCode_Target_5XX_Count"
namespace = "AWS/ECS"
period = 60
statistic = "Sum"
threshold = 10
alarm_description = "auth-service returning >10 5xx errors per minute"
treat_missing_data = "notBreaching"
dimensions = {
ServiceName = "${var.name}-${var.environment}-auth"
ClusterName = aws_ecs_cluster.main.name
}
alarm_actions = [aws_sns_topic.alerts.arn]
}
# Request latency alarm
resource "aws_cloudwatch_metric_alarm" "auth_service_latency" {
alarm_name = "${var.name}-auth-service-p99-latency"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 3
extended_statistic = "p99"
metric_name = "TargetResponseTime"
namespace = "AWS/ECS"
period = 60
threshold = 0.5 # 500ms p99
alarm_description = "auth-service p99 latency > 500ms"
dimensions = {
ServiceName = "${var.name}-${var.environment}-auth"
ClusterName = aws_ecs_cluster.main.name
}
alarm_actions = [aws_sns_topic.alerts.arn]
}
Security Group Configuration
# Internal services communicate on their container ports
resource "aws_security_group" "ecs_tasks" {
name = "${var.name}-${var.environment}-ecs-tasks"
vpc_id = var.vpc_id
description = "ECS tasks — allow internal service communication"
# Allow all traffic within the security group (service-to-service)
ingress {
from_port = 0
to_port = 65535
protocol = "tcp"
self = true # Other tasks in the same SG
description = "Internal service-to-service via Service Connect"
}
# Allow inbound from ALB for public-facing services
ingress {
from_port = 3000
to_port = 3000
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
description = "ALB to api-service"
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = var.common_tags
}
Cost Estimates
| Component | Cost |
|---|---|
| Service Connect proxy sidecar | Included in Fargate task CPU/memory |
| Cloud Map HTTP namespace | $0.10/1K API calls (usually < $5/month) |
| CloudWatch metrics | $0.30/metric/month (10 services ≈ $10/month) |
| Additional Fargate CPU for sidecar | ~64 CPU units / task (minimal) |
| Total vs ALB for internal | ALB: $16/month fixed + $0.008/LCU vs Service Connect: ~$10–$15/month in metrics |
See Also
- AWS ECS Fargate Production — Full ECS production setup
- AWS CloudWatch Observability — Monitoring the services
- Kubernetes Helm Charts — If you outgrow ECS
- Terraform State Management — Managing multi-service Terraform
Working With Viprasol
We design and deploy ECS microservice architectures with Service Connect for B2B SaaS products — from simple 2-service setups through complex 10+ service platforms. Our cloud team has migrated multiple products from ALB-based internal routing to Service Connect with zero-downtime deployments.
What we deliver:
- Service Connect namespace and cluster Terraform modules
- Task definition templates with named port mappings
- Internal service client libraries with timeout and retry
- CloudWatch alarms for per-service error rate and latency
- Security group rules for internal service communication
See our cloud infrastructure services or contact us to design your ECS microservice networking.
About the Author
Viprasol Tech Team
Custom Software Development Specialists
The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.
Need DevOps & Cloud Expertise?
Scale your infrastructure with confidence. AWS, GCP, Azure certified team.
Free consultation • No commitment • Response within 24 hours
Making sense of your data at scale?
Viprasol builds end-to-end big data analytics solutions — ETL pipelines, data warehouses on Snowflake or BigQuery, and self-service BI dashboards. One reliable source of truth for your entire organisation.