AWS ECS with Twingate (Headless & Userspace)
Secure private access for ECS workloads using Service Accounts
Summary
Twingate can be used to provide secure network access for workloads running on AWS ECS using service accounts. This could be a service (frontend app, AI agent, etc.) that needs private and secure access to a backend.
Depending on the ECS launch type, workloads can run either:
- Userspace (HTTP/HTTPS proxy mode) — supported on ECS Fargate
- Full headless client (TUN mode) — supported on ECS on EC2 (Managed Instances)
This guide explains the differences, limitations, and recommended deployment models for each.
AWS ECS Fargate
Supported Mode: Userspace (HTTP/s Proxy Only)
ECS Fargate does not currently support the Linux kernel capabilities required for full network tunneling.
Specifically, the Twingate Client requires:
- Access to
/dev/net/tunfor device access CAP_NET_ADMINto create interfaces, routes, and IP rules for that device
AWS Fargate does not allow adding kernel capabilities beyond SYS_PTRACE.
As a result:
- ❌ Full TUN mode is not supported on Fargate
- ✅ Userspace (HTTP/HTTPS proxy mode) is supported
This aligns with Twingate’s userspace networking model, where traffic is explicitly sent through an HTTP proxy instead of being intercepted at the kernel level.
Fargate Architecture
In a typical Fargate task:
- The Twingate Client runs in userspace mode (
--tun off) - Application containers explicitly send HTTP/HTTPS traffic through the proxy
- Access is enforced via Zero Trust (what the Client has access to)
Because the Twingate Client container does not expose a shell, it is started using the twingated entrypoint with arguments passed at runtime.
Service Account Injection (Why an Init Container Is Still Required)
AWS Secrets Manager injects secrets into ECS containers as environment variables, not as files.
The Twingate Client expects the service key at:
/etc/twingate/service_key.jsonBecause:
- The client image does not provide a writable entrypoint
- The secret must exist as a file before
twingatedstarts - Secrets cannot be securely mounted as files directly in Fargate
An init container is used to:
- Read the service key from Secrets Manager (via env var)
- Write it to
/etc/twingate/service_key.json - Share the file with the Twingate Client via a volume
This pattern is functionally equivalent to mounting a secret volume and is the recommended approach on ECS Fargate.
Example Fargate Task Definition
Twingate Userspace Client
{ "containerDefinitions": [ { "command": [ "sh", "-lc", "set -e; mkdir -p /etc/twingate; printf '%s' \"$SERVICE_KEY_JSON\" > /etc/twingate/service_key.json; chmod 0444 /etc/twingate/service_key.json" ], "cpu": 0, "environment": [], "essential": false, "image": "alpine:latest", "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/tg-userspace-client", "awslogs-create-group": "true", "awslogs-region": "<region>", "awslogs-stream-prefix": "init" } }, "mountPoints": [ { "containerPath": "/etc/twingate", "readOnly": false, "sourceVolume": "twingate-etc" } ], "name": "init-write-key", "portMappings": [], "secrets": [ { "name": "SERVICE_KEY_JSON", "valueFrom": "arn:aws:secretsmanager:<region>:<account-id>:secret:twingate/service-key" } ], "systemControls": [], "volumesFrom": [] }, { "command": [ "--http-proxy", "0.0.0.0:9999", "--tun", "off" ], "cpu": 0, "dependsOn": [ { "condition": "SUCCESS", "containerName": "init-write-key" } ], "entryPoint": [ "twingated" ], "environment": [], "essential": true, "image": "twingate/client:latest", "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/tg-userspace-client", "awslogs-create-group": "true", "awslogs-region": "<region>", "awslogs-stream-prefix": "twingate" } }, "mountPoints": [ { "containerPath": "/etc/twingate", "readOnly": false, "sourceVolume": "twingate-etc" } ], "name": "tg-userspace-client", "portMappings": [ { "containerPort": 9999, "hostPort": 9999, "protocol": "tcp" } ], "systemControls": [], "volumesFrom": [] }, { "command": [ "sh", "-lc", "apk add --no-cache curl >/dev/null; TARGETS='http://my-private-backend.int https://ipinfo.io/what-is-my-ip'; i=0; while true; do i=$((i+1)); echo \"[$(date -Iseconds)] attempt=$i\"; for url in $TARGETS; do echo \"→ $url\"; curl -vk --max-time 20 --proxy http://127.0.0.1:9999 \"$url\" || true; done; echo 'sleeping 300s'; sleep 300; done" ], "cpu": 0, "dependsOn": [ { "condition": "START", "containerName": "tg-userspace-client" } ], "environment": [], "essential": false, "image": "alpine:latest", "logConfiguration": { "logDriver": "awslogs", "options": { "awslogs-group": "/ecs/tg-userspace-client", "awslogs-create-group": "true", "awslogs-region": "<region>", "awslogs-stream-prefix": "tester" } }, "mountPoints": [], "name": "tg-userspace-tester", "portMappings": [], "systemControls": [], "volumesFrom": [] } ], "cpu": "1024", "executionRoleArn": "arn:aws:iam::<account-id>:role/ecsTaskExecutionAndSecretsRead", "family": "tg-userspace-client", "memory": "2048", "networkMode": "awsvpc", "requiresCompatibilities": [ "FARGATE" ], "volumes": [ { "host": {}, "name": "twingate-etc" } ]}AWS ECS on EC2 (and Managed Instances)
ECS on EC2 runs containers on a fleet of customer-managed Linux hosts.
This environment does support:
- Kernel capabilities
- Privileged containers
- System services (systemd)
As a result, both networking modes are supported.
Option 1: Full Headless Client (Recommended at Scale)
For larger deployments, Twingate recommends running one client per EC2 host, not one per application task.
This is done by:
- Installing the Twingate Client directly on the EC2 host
- Running it as a systemd service
- Authenticating using a service account
Benefits:
- One client per host (not per task)
- Full network tunneling
- Lower operational overhead
- Better performance for non-HTTP traffic
This deployment model is fully supported and commonly used.
Sidecar Clients at Scale
While possible, running a Twingate Client as a sidecar for every ECS task is not recommended at scale. It increases connection overhead (which risks API throttling), resource usage, and operational complexity.
For ECS on EC2, Twingate recommends one client per host, authenticated via a service account and managed as a system service. This provides better performance and simpler operations.
Option 2: Userspace Mode on EC2
If workloads only require HTTP/HTTPS access, userspace mode can also be used on ECS EC2.
This may be appropriate for:
- Least-privilege environments
- Sidecar-style deployments
- Incremental adoption
However, for most EC2-based deployments, full headless mode is preferred.
Choosing the Right Model
| Environment | Supported Mode | Recommendation |
|---|---|---|
| ECS Fargate | Userspace only | HTTP/HTTPS workloads |
| ECS on EC2 | Full headless | Default / recommended |
| ECS on EC2 | Userspace | HTTP-only or least-privilege |
Troubleshooting
If access fails in ECS deployments:
- Confirm the Twingate Client is running and authenticated
- Verify the service key file exists at
/etc/twingate/service_key.json - Confirm applications are explicitly using the proxy (userspace mode)
- Check ECS task logs for startup or auth errors
- Review Recent Activity for the Resource in the Admin Console
For Fargate specifically:
- Verify traffic is HTTP/HTTPS only
- Ensure no application attempts raw TCP or UDP connections
- Confirm Secrets Manager permissions on the execution role
For additional help, see the Twingate Troubleshooting Guide.
Related Resources
Last updated 14 minutes ago