Madushan Pathirana
08/20/2025, 7:18 PMMarvin
08/20/2025, 7:18 PMMarvin
08/20/2025, 7:24 PMpip install "prefect[aws]"
- Create an ECS work pool and set its job template:
prefect work-pool create my-ecs-pool --type ecs
prefect work-pool get-default-base-job-template --type ecs > ecs-template.json
# edit ecs-template.json: cluster, launch_type (FARGATE or EC2), cpu, memory,
# execution_role_arn, task_role_arn, network_configuration, container_name, etc.
prefect work-pool create my-ecs-pool --type ecs --base-job-template ecs-template.json --overwrite
- Start the worker on your EC2 (with AWS creds available via instance profile or env vars), pointing at your self-hosted server:
export PREFECT_API_URL=http://<your-prefect-server>:4200/api
prefect worker start -p my-ecs-pool
2) Run the worker as an ECS service (if you really want everything “in ECS”)
- Build a tiny image that just runs the worker:
FROM python:3.11-slim
RUN pip install "prefect[aws]"
ENV PREFECT_API_URL=http://<your-prefect-server>:4200/api
CMD ["prefect", "worker", "start", "-p", "my-ecs-pool", "-n", "ecs-worker"]
- Push that image to ECR and have your engineer create a long‑running ECS service for it with an IAM role that allows RunTask/RegisterTaskDefinition/DescribeTasks and iam:PassRole. You still need the ECS work pool and flow images as above.
Tie your deployments to ECS
In your deployment call, reference the ECS work pool and your flow image. For example:
from prefect import flow
@flow
def myflow():
...
(
myflow
.from_source("<https://github.com/owner/repo>", entrypoint="flows/my.py:myflow")
.deploy(
name="prod",
work_pool_name="my-ecs-pool",
job_variables={
"image": "<http://123456789012.dkr.ecr.us-east-1.amazonaws.com/flows:abc123|123456789012.dkr.ecr.us-east-1.amazonaws.com/flows:abc123>",
"cluster": "my-ecs-cluster",
"launch_type": "FARGATE",
"cpu": 1024,
"memory": 2048,
"execution_role_arn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"task_role_arn": "arn:aws:iam::123456789012:role/myFlowTaskRole",
"network_configuration": {
"awsvpcConfiguration": {
"subnets": ["subnet-abc", "subnet-def"],
"securityGroups": ["sg-123"],
"assignPublicIp": "ENABLED"
}
},
"container_name": "flow"
},
)
)
You can set most of these once in the work pool’s base job template so deployments only override the image tag.
IAM quick checklist
- Worker (wherever it runs): ecs:RunTask, ecs:DescribeTasks, ecs:DescribeTaskDefinition, ecs:RegisterTaskDefinition (if you let Prefect auto-register), iam:PassRole for your task/execution roles.
- Execution role on the task: AmazonECSTaskExecutionRolePolicy (includes ECR pull + CloudWatch logs).
- Task role: whatever your flow code needs (S3, SNS, etc.).
Recommendation for your caseMarvin
08/20/2025, 7:24 PMMadushan Pathirana
08/20/2025, 7:50 PMMarvin
08/20/2025, 7:50 PMMarvin
08/20/2025, 7:53 PMprefect work-pool set-concurrency-limit my-ecs-pool 10
- If you expect big bursts, run multiple workers against the same pool (on EC2 or ECS); Prefect will distribute runs across them.
How to do ECS Fargate cleanly
1) Create an ECS work pool and set a Fargate job template:
- Create the pool:
prefect work-pool create my-ecs-pool --type ecs
prefect work-pool get-default-base-job-template --type ecs > ecs-template.json
- In ecs-template.json, set at minimum:
- launch_type: "FARGATE"
- cluster: "my-ecs-cluster"
- cpu/memory (e.g., 1024/2048)
- container_name (e.g., "flow")
- execution_role_arn (ECR pull + CloudWatch logs)
- task_role_arn (permissions your flow needs)
- network_configuration:
{
"awsvpcConfiguration": {
"subnets": [
"subnet-abc",
"subnet-def"
],
"securityGroups": [
"sg-123"
],
"assignPublicIp": "ENABLED"
}
}
- Apply it:
prefect work-pool create my-ecs-pool --type ecs --base-job-template ecs-template.json --overwrite
2) Start a worker
- Easiest: run it on your existing EC2 (with an instance profile or AWS creds), making sure it can reach your Prefect Server API:
export PREFECT_API_URL=http://<your-prefect-server>:4200/api
pip install "prefect[aws]"
prefect worker start -p my-ecs-pool
- Or, run the worker as its own ECS service using a tiny image:
FROM python:3.11-slim
RUN pip install "prefect[aws]"
ENV PREFECT_API_URL=http://<your-prefect-server>:4200/api
CMD ["prefect", "worker", "start", "-p", "my-ecs-pool", "-n", "ecs-worker"]
Your engineer creates a long‑running ECS service from that image with an IAM role that can ecs:RunTask, ecs:DescribeTasks, ecs:RegisterTaskDefinition (if you let Prefect auto-register), and iam:PassRole.
3) Point deployments at the pool and your ECR flow images
- In code:
from prefect import flow
@flow
def myflow(): ...
(
myflow
.from_source("<https://github.com/owner/repo>", entrypoint="flows/my.py:myflow")
.deploy(
name="prod",
work_pool_name="my-ecs-pool",
job_variables={
"image": "<http://123456789012.dkr.ecr.us-east-1.amazonaws.com/flows:abc123|123456789012.dkr.ecr.us-east-1.amazonaws.com/flows:abc123>",
"cluster": "my-ecs-cluster",
"launch_type": "FARGATE",
"cpu": 1024,
"memory": 2048,
"execution_role_arn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"task_role_arn": "arn:aws:iam::123456789012:role/myFlowTaskRole",
"network_configuration": {
"awsvpcConfiguration": {
"subnets": ["subnet-abc","subnet-def"],
"securityGroups": ["sg-123"],
"assignPublicIp": "ENABLED"
}
},
"container_name": "flow"
},
)
)
You can keep most fields in the pool template and only override the image per deployment.
Triggering on-demand via API
- From your systems, create runs when needed:
- CLI:
prefect deployment run myflow/prod --param key=value
- Python:
from prefect.deployments import run_deployment
run = run_deployment(name="myflow/prod", parameters={"key": "value"})
- Or call the Prefect Server “create flow run from deployment” endpoint if you prefer raw HTTP.
IAM checklist
- Worker’s role: ecs:RunTask, ecs:DescribeTasks, ecs:DescribeTaskDefinition, ecs:RegisterTaskDefinition (optional), iam:PassRole to your task/execution roles.
- Task execution role: AmazonECSTaskExecutionRolePolicy (ECR pull + CloudWatch logs).Marvin
08/20/2025, 7:53 PMMadushan Pathirana
08/20/2025, 7:56 PMMarvin
08/20/2025, 7:56 PMMarvin
08/20/2025, 7:56 PMprefect work-pool set-concurrency-limit my-docker-pool 5
Set this to something your EC2 instance can actually handle.
- Enforce per-container limits in the Docker job template:
{
"image": "<http://123456789012.dkr.ecr.us-east-1.amazonaws.com/flows:abc123|123456789012.dkr.ecr.us-east-1.amazonaws.com/flows:abc123>",
"mem_limit": "1g",
"container_create_kwargs": {
"nano_cpus": 2000000000 // ~2 vCPU
},
"auto_remove": true,
"stream_output": true
}
Use mem_limit
and CPU controls (nano_cpus
via container_create_kwargs
) to keep a single job from eating the whole box.
- Scale horizontally or upsize:
- Run multiple workers on separate EC2 instances against the same pool, or use a larger instance type.
- Run the worker as a service with restarts:
- systemd or Docker --restart=always
helps it recover if the OS kills it. Not magic, but better than nothing.
Since you want on-demand bursts, ECS Fargate is the safer option: the worker remains light, and the heavy lifting happens in Fargate tasks that scale independently of your EC2. Less chance of turning your EC2 into a very expensive space heater.