Skip to content

Deploy Containers with ECS Anywhere

If you already use Amazon ECS, you can attach Heata VMs as external instances in your ECS cluster. Your existing task definitions, services, and tooling work as-is — the only difference is that tasks run on Heata hardware using the EXTERNAL launch type.


How it works

AWS Cloud                              Heata infrastructure
┌──────────────────────┐               ┌───────────────────────┐
│                      │               │                       │
│  ECS Control Plane   │               │  Heata VM             │
│  (your cluster)      │   SSM Agent   │  ┌─────────────────┐  │
│                      │ <───────────> │  │ Docker          │  │
│  Task definitions    │               │  │ ECS Agent       │  │
│  Services            │   pull/run    │  │ SSM Agent       │  │
│  Scheduling          │ ────────────> │  │                 │  │
│                      │               │  │ Your containers │  │
│                      │               │  └─────────────────┘  │
└──────────────────────┘               └───────────────────────┘

The ECS agent on each Heata VM communicates with your ECS cluster via AWS Systems Manager (SSM). No inbound ports or VPN to AWS required.


Prerequisites

  • An AWS account with ECS access
  • AWS CLI configured locally
  • A Heata spot VM allocation (we provide the VMs)

Step 1: Create the IAM role (one-time)

# Create trust policy
cat > ssm-trust-policy.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Principal": { "Service": "ssm.amazonaws.com" },
    "Action": "sts:AssumeRole"
  }
}
EOF

# Create role and attach policies
aws iam create-role \
  --role-name ecsAnywhereRole \
  --assume-role-policy-document file://ssm-trust-policy.json

aws iam attach-role-policy \
  --role-name ecsAnywhereRole \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

aws iam attach-role-policy \
  --role-name ecsAnywhereRole \
  --policy-arn arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role

Step 2: Create an SSM activation

Generate an activation code that your Heata VMs will use to register:

aws ssm create-activation \
  --iam-role ecsAnywhereRole \
  --registration-limit 10 \
  --default-instance-name "heata-ecs-node" \
  --region eu-west-2

This returns an ActivationId and ActivationCode. You'll pass these to the VM boot script.

Step 3: Create the ECS cluster

aws ecs create-cluster --cluster-name heata-cluster

No special configuration needed — the cluster becomes ECS Anywhere capable once external instances register.

Step 4: VM boot script

Use this as a Heata VM first-boot script (see Spot-like VMs for how boot scripts work). It installs Docker, SSM Agent, and the ECS agent, then registers the VM with your ECS cluster:

#!/bin/bash
# scripts/first-boot/01-ecs-anywhere.sh
set -euo pipefail
exec > >(tee /var/log/ecs-anywhere-setup.log) 2>&1

# Configuration — replace with your values
REGION="eu-west-2"
CLUSTER="heata-cluster"
ACTIVATION_ID="your-activation-id"
ACTIVATION_CODE="your-activation-code"

# Pre-create ECS config
mkdir -p /etc/ecs
cat > /etc/ecs/ecs.config <<EOF
ECS_CLUSTER=${CLUSTER}
ECS_LOGLEVEL=info
ECS_ENABLE_TASK_IAM_ROLE=true
ECS_ENABLE_TASK_IAM_ROLE_NETWORK_HOST=true
EOF

# Configure iptables for task IAM roles
sysctl -w net.ipv4.conf.all.route_localnet=1
iptables -t nat -A PREROUTING -p tcp -d 169.254.170.2 --dport 80 \
  -j DNAT --to-destination 127.0.0.1:51679
iptables -t nat -A OUTPUT -d 169.254.170.2 -p tcp -m tcp --dport 80 \
  -j REDIRECT --to-ports 51679

# Download and run the official ECS Anywhere installer
curl --proto "https" -o /tmp/ecs-anywhere-install.sh \
  "https://amazon-ecs-agent.s3.amazonaws.com/ecs-anywhere-install-latest.sh"

bash /tmp/ecs-anywhere-install.sh \
  --region "$REGION" \
  --cluster "$CLUSTER" \
  --activation-id "$ACTIVATION_ID" \
  --activation-code "$ACTIVATION_CODE"

echo "[$(date)] ECS Anywhere setup complete" >> /var/log/heata-client.log

Once the VM boots, it appears in your ECS cluster as an external container instance.

Step 5: Deploy a task

Register a task definition targeting EXTERNAL:

{
  "family": "my-app",
  "requiresCompatibilities": ["EXTERNAL"],
  "networkMode": "bridge",
  "containerDefinitions": [
    {
      "name": "my-app",
      "image": "your-registry.io/my-app:latest",
      "cpu": 256,
      "memory": 512,
      "essential": true,
      "portMappings": [
        {
          "containerPort": 8080,
          "hostPort": 8080,
          "protocol": "tcp"
        }
      ]
    }
  ]
}
# Register the task definition
aws ecs register-task-definition --cli-input-json file://task-def.json

# Run a one-off task
aws ecs run-task \
  --cluster heata-cluster \
  --task-definition my-app:1 \
  --launch-type EXTERNAL

# Or create a long-running service
aws ecs create-service \
  --cluster heata-cluster \
  --service-name my-app \
  --task-definition my-app:1 \
  --launch-type EXTERNAL \
  --desired-count 2

Limitations

A few things to be aware of when using the EXTERNAL launch type:

  • Network mode: bridge, host, or none only (awsvpc is not supported)
  • No load balancer integration: You manage routing to external instances yourself
  • No service discovery: Use your own DNS or service mesh
  • No EFS volumes: Use local storage or mount your own network storage

For most batch and background workloads these limitations don't matter. For services that need ingress, load balancing, or service discovery, Kubernetes is a better fit.


Support

If you have questions or run into issues:

  • Email: techsupport@heata.co
  • Slack: We can set up a shared channel with your team
  • Std Response time: Within 4 business hours for production issues, speak to us about other options.

Your compute on Heata provides free hot water for families around the UK.