# amazon-ecs-deploy-task-definition: shipping a new task definition from a GitHub Actions workflow

> The aws-actions/amazon-ecs-deploy-task-definition action registers an Amazon ECS task definition and deploys it to a service or standalone task, with support for CodeDeploy, managed tags and EBS volumes. It suits teams that keep their task definition in git, and it is not a general purpose ECS management tool.

**aws-actions/amazon-ecs-deploy-task-definition** — Registers an Amazon ECS task definition and deploys it to an ECS service.

- Repository: https://github.com/aws-actions/amazon-ecs-deploy-task-definition
- Stars: 764 · Forks: 272
- Language: JavaScript
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/aws-actions-amazon-ecs-deploy-task-definition

## One action, one transition: register a revision, then update the service

The scope is narrow on purpose. The action registers an Amazon ECS task definition and deploys it to an ECS service, and that is the whole job. In a workflow it looks like this:

```yaml
    - name: Deploy to Amazon ECS
      uses: aws-actions/amazon-ecs-deploy-task-definition@v2
      with:
        task-definition: task-definition.json
        service: my-service
        cluster: my-cluster
        wait-for-service-stability: true
```

Four inputs do the work. The task-definition points at a JSON file, the service and cluster name the target, and wait-for-service-stability decides whether the step blocks until ECS reports the service stable. The reference version is v2, which is the major tag the documentation uses throughout, and the most recent release on the repository is v2.6.4.

The action file is where the full input and output list lives, and the documentation says so directly rather than duplicating it. That is a reasonable division of labour for a project with this many inputs, though it does mean a workflow author has to jump between two documents to know what a step can do.

## Treating the task definition as code is the pattern everything else builds on

The documentation is explicit that you should treat the task definition as code by checking it into your git repository as a JSON file, so that changes to container images, environment variables, CPU or memory are deployed by editing the file and pushing a commit. That advice is the load-bearing part of this action. Without a file in git, the action has nothing to read.

Two AWS CLI commands set that file up. To pull an existing definition down as a starting point:

```bash
aws ecs describe-task-definition \
   --task-definition my-task-definition-family \
   --query taskDefinition > task-definition.json
```

And to start from an empty skeleton:

```bash
aws ecs register-task-definition \
   --generate-cli-skeleton > task-definition.json
```

The pulled version carries values from your live account, and the documentation tells you what to strip: account IDs by removing the taskDefinitionArn attribute, and the executionRoleArn and taskRoleArn attributes, whose values should be role names rather than role ARNs. That step matters more than it looks. A task definition file in git that contains account IDs is a file that breaks the moment you use it in a second account, and role ARNs will not resolve there either.

If you would rather not commit the file, the documentation shows a workflow step that downloads it at run time, using the same describe-task-definition command inside a run block.

## A unique image tag per commit is what makes rollback possible

The deployment guidance is blunt about container images. Each time the workflow builds a new image for deployment, a new container image ID should be generated, and the example given is the commit ID as the tag rather than updating a latest tag. The reason is stated directly: a unique image ID for each deployment is what allows rolling back to a previous image.

The full chain is the companion action aws-actions/amazon-ecs-render-task-definition, which rewrites the image field in the task definition file before the deploy step runs. The documented sequence configures AWS credentials, logs into ECR, builds and pushes an image tagged with the commit SHA, passes the resulting image reference into the render step, and hands the rendered task definition to the deploy step:

```yaml
    - name: Build, tag, and push image to Amazon ECR
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: my-ecr-repo
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT
```

This is a two action design, not one. The render action owns the substitution, the deploy action owns the registration and the update, and the workflow wires them together. A team that only wants to redeploy an unchanged image can skip the render step entirely and point task-definition at the checked in file.

## Tags are opt-in, and there are two separate switches for them

ECS has two distinct tag concepts and the action exposes them as two separate inputs, which is a detail that catches people out. The first is ECS-managed tags, the aws:ecs:serviceName and aws:ecs:clusterName values that ECS itself attaches to the tasks it launches. Turning them on is a single input:

```yaml
      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: task-definition.json
          service: my-service
          cluster: my-cluster
          wait-for-service-stability: true
          enable-ecs-managed-tags: true
```

The second is tag propagation from the service, which copies your own custom tags onto the tasks. The input takes the value SERVICE:

```yaml
      - name: Deploy Amazon ECS task definition
        uses: aws-actions/amazon-ecs-deploy-task-definition@v2
        with:
          task-definition: task-definition.json
          service: my-service
          cluster: my-cluster
          wait-for-service-stability: true
          propagate-tags: SERVICE
```

Both default to off, so a team that wants either behaviour has to turn it on explicitly. That is the safer default, since managed tags on every task can surprise a cost or a permissions review that assumed tasks carried no tags.

## EBS volumes work for services and standalone tasks, with two different input pairs

The action supports configuring Amazon EBS volumes, and the documentation splits this into two cases because the underlying ECS calls differ. For a service, the update path takes a volume name and a volume configuration as JSON:

```yaml
    - name: Deploy to Amazon ECS with EBS Volume
      uses: aws-actions/amazon-ecs-deploy-task-definition@v2
      with:
        task-definition: task-definition.json
        service: my-service
        cluster: my-cluster
        wait-for-service-stability: true
        service-managed-ebs-volume-name: "ebs1"
        service-managed-ebs-volume: '{"sizeInGiB": 30, "volumeType": "gp3", "encrypted": true, "roleArn":"arn:aws:iam::<account-id>:role/ebs-role"}'
```

The volume configuration is a JSON string on one line, carrying the size in GiB, the volume type, the encryption flag and the role ARN that ECS uses to manage the volume. The role ARN in the example carries an account-id placeholder, so it has to be filled in per account, which means it does not belong in a shared task definition file.

There is a precondition the documentation calls out: the task definition must itself declare the volume with configuredAtLaunch set to true.

```json
    ...
    "volumes": [
        {
            "name": "ebs1",
            "configuredAtLaunch": true
        }
    ],
    ...
```

For a standalone task, the deployment goes through the run task call instead, and the documentation uses a separate set of inputs for it. The two paths are not interchangeable, so a workflow that runs one-off tasks needs its own step shape.

## What the action cannot do for you, and where the manual path is simpler

This action does one transition. It registers a task definition revision and updates a service or runs a task, and it can wait for the service to stabilise. It does not create services, does not create clusters, does not scale, and does not roll back a deployment that went bad. If the pipeline fails after the service update, the action does not put the previous revision back. The documentation recommends unique image tags precisely so that a human or a later step can roll forward, or roll back, by deploying a different known-good image.

The manual alternative is not hypothetical, it is the same AWS CLI. The documentation itself uses aws ecs describe-task-definition and aws ecs register-task-definition to produce the file the action then deploys. A team that would rather keep a shell script than a workflow step can call the ECS API directly and skip the action entirely. The difference in approach is where the state lives: the action keeps a task definition file in git and makes the file the source of truth, while a shell script keeps the AWS account as the source of truth and the pipeline as a sequence of API calls.

The action also has a specific failure mode worth knowing. wait-for-service-stability makes the step block on the deployment reaching a stable state, and a service that never stabilises will hold the job open rather than fail fast. For a long-running production service, that is usually the behaviour you want, but it turns a deployment problem into a job that hangs instead of an error you see quickly.

## A small JavaScript action with a checked in build and a published changelog

The repository is compact. The entry point is index.js, the action metadata is action.yml, and the top-level entries include a dist/ directory, an eslint config, a Jest test file, a CHANGELOG.md, a THIRD-PARTY file and a .mergify.yml. The package metadata gives the shape of the build:

```json
  "main": "index.js",
  "scripts": {
    "lint": "eslint **.js",
    "package": "ncc build index.js -o dist",
    "test": "eslint **.js && jest --coverage"
  },
```

The dist/ directory is the compiled bundle that GitHub Actions actually runs, produced by ncc from index.js, which is the standard pattern for a JavaScript action that has to run without an install step on the runner. The runtime dependencies are few and specific: @actions/core for inputs and outputs, the AWS SDK clients for ECS and CodeDeploy, and yaml for parsing.

The presence of the CodeDeploy client explains the AWS CodeDeploy support section in the documentation, which is the blue and green or linear deployment path for ECS services, used when you want a deployment to be a managed rollout rather than a plain service update. The CodeDeploy flow needs its own configuration, and the action's documentation covers it separately from the basic path.

Licensing is MIT, per both the LICENSE file in the tree and the package metadata, and the documentation has a license summary section. The repository also has a security disclosures section, which is the usual place to find how to report a problem. The last push was on 2026-10-09, the same day as the v2.6.4 release, and the previous patch release, v2.6.3, is dated 2026-07-01.

## Conclusion

amazon-ecs-deploy-task-definition is the right action for a team that already keeps an ECS task definition as a JSON file in git and wants a push to become a new registered revision and a service update, because the action exists to do exactly that one transition and to wait for the service to become stable. It is the wrong tool for a cluster you administer by hand, for anything outside ECS, and for a pipeline that has no task definition file yet. Before you wire it into a workflow, check the three things the documentation does not spell out for you: that your task definition file has been cleaned of account IDs and role ARNs, since the file is checked into git and those values are environment specific; that the container image tag is unique per commit, because reusing latest is what removes your ability to roll back; and that the IAM permissions your workflow role holds cover the register, update and wait calls this action makes on your behalf.

## FAQ

### What does the amazon-ecs-deploy-task-definition GitHub Action do?

It registers an Amazon ECS task definition and deploys it to an ECS service, with a separate path for standalone tasks. The basic step takes task-definition, service, cluster and wait-for-service-stability, and the action file holds the full list of inputs and outputs.

### How do I get a task-definition.json file for the action to use?

The documentation shows aws ecs describe-task-definition with a --query taskDefinition redirect to write the file, or aws ecs register-task-definition with --generate-cli-skeleton to start an empty one. Account IDs are removed by deleting the taskDefinitionArn attribute, and the executionRoleArn and taskRoleArn values should be role names rather than role ARNs.

### Does the action support EBS volumes for my ECS tasks?

Yes, for both services and standalone tasks. A service deployment takes service-managed-ebs-volume-name and a service-managed-ebs-volume JSON string, and the task definition itself has to declare the volume with configuredAtLaunch set to true.

## Sources

- [aws-actions/amazon-ecs-deploy-task-definition on GitHub](https://github.com/aws-actions/amazon-ecs-deploy-task-definition)
- [Issues](https://github.com/aws-actions/amazon-ecs-deploy-task-definition/issues)
- [License: MIT](https://github.com/aws-actions/amazon-ecs-deploy-task-definition/blob/master/LICENSE)
- [README](https://github.com/aws-actions/amazon-ecs-deploy-task-definition/blob/master/README.md)
- [Releases](https://github.com/aws-actions/amazon-ecs-deploy-task-definition/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/aws-actions-amazon-ecs-deploy-task-definition
