---
source: https://carbone.io/documentation/developer/self-hosted-deployment/deploy-on-aws-ecs.html
title: "Carbone on Amazon Elastic Container Service"
description: "Generate and manage your reports inside your own Amazon ECS Cluster"
generated_at: "2026-07-13"
---

# Carbone on Amazon Elastic Container Service

Generate and manage your reports inside your own Amazon ECS Cluster

## Introduction

Setting up Carbone in an ECS environment is very simple and effective.

Here's the recommended configuration:

![Carbone on ECS architecture](/img/doc/aws-ecs-architecture.svg)

> ℹ️ **Note:** To use Carbone Enterprise Edition on Amazon Elastic Container Service, you need a Carbone license. [Talk to us](https://carboneio.pipedrive.com/scheduler/5Rzzbxu6/carbone-on-premiseaws-presentation) to find out more.

## Quickstart

### Create ECS Cluster

To set up Carbone in your environment, the first step is to create your cluster. To do this, connect to the [Amazon Elastic Container Service console](https://console.aws.amazon.com/ecs/v2/clusters) and create your cluster:

![Create Carbone Cluster](/img/doc/aws-ecs-cluster.png)

### Create IAM role

You then need to prepare a role that will be used by your Carbone container.

For this example, we suggest you give access rights to CloudWatch and to read secrets for license storage.

You must first create the `carbone_service_role` with Elastic Container Service Task trusted entity : ![Create Role](/img/doc/aws-ecs-role-create.png)

Then, you must assign `AmazonECSTaskExecutionRolePolicy`, `CloudWatchLogsFullAccess` and custom `ReadSecret` permissions policies : ![Assign permission to role](/img/doc/aws-ecs-role-permissions.png)

### Storing Carbone License

To store secrets properly, we recommend using AWS Secrets Manager. You'll need to create a new secret from the [AWS Secrets Manager Console](https://console.aws.amazon.com/secretsmanager/listsecrets).

Insert your license as `Other type of secret` : ![Storing Carbone licence](/img/doc/aws-ecs-licence.png)

### Create Task definition

You now need to create a task definition for the Carbone service. Go to the [Task definitions console](https://console.aws.amazon.com/ecs/v2/task-definitions), and create a task. Here is an example of a json task definition:

```json
{
    "family": "carboneService",
    "containerDefinitions": [
        {
            "name": "Carbone",
            "image": "carbone/carbone-ee:full",
            "cpu": 0,
            "portMappings": [
                {
                    "containerPort": 4000,
                    "hostPort": 4000,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "environment": [
                {
                    "name": "CARBONE_STUDIO",
                    "value": "true"
                }
            ],
            "mountPoints": [],
            "volumesFrom": [],
            "secrets": [
                {
                    "name": "CARBONE_LICENSE",
                    "valueFrom": <REPLACE WITH YOUR LICENCE SECRET ARN>
                }
            ],
            "stopTimeout": 20,
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "awslog-carbone",
                    "awslogs-create-group": "true",
                    "awslogs-region": "eu-west-3",
                    "awslogs-stream-prefix": "Carbone"
                }
            },
            "systemControls": []
        }
    ],
    "executionRoleArn": <REPLACE WITH YOUR ROLE ARN>,
    "networkMode": "awsvpc",
    "placementConstraints": [],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "1024",
    "memory": "2048",
    "runtimePlatform": {
        "cpuArchitecture": "ARM64",
        "operatingSystemFamily": "LINUX"
    }
}
```

### Create Service

Finally, all that's left to do is create the service in your cluster using the task definition you've just created.

You can then configure the number of tasks you need, autoscaling, an `Application Load Balancer`, ...

Et voilà 🎉

## Configuration

### Storage backends

To use Carbone in production, you need to configure data persistence for template and rendering storage.

There are two possible solutions in the ECS environment: using S3 buckets or configuring an `Elastic File System` shared volume.

> ⚠️ **Warning:** We recommend using S3 rather than EFS. If you run multiple instances with [template management](/documentation/developer/on-premise-installation/template-management.md) enabled, EFS is **not supported** as a storage backend — use S3 instead.

#### Storing persistent data on S3 bucket

To store your data on S3 buckets, you need to :

-   Create 2 buckets, one for storing your templates, and one for storing renderings
-   Create a user with a pair of API keys (KEY\_ID and KEY\_SECRET) with read and write rights to these buckets.
-   In the task definition, you need to add the following environment variables:
    
    ```bash
    AWS_ENDPOINT_URL=s3.amazonaws.com
    AWS_REGION=<S3 REGION>
    AWS_ACCESS_KEY_ID=<YOUR KEY_ID>
    AWS_SECRET_ACCESS_KEY=<YOUR KEY_SECRET>
    BUCKET_TEMPLATES=<YOUR TEMPLATES BUCKET NAME>
    BUCKET_RENDERS=<YOUR RENDERS BUCKET NAME>
    ```
    

#### Storing persistent data on shared Elastic File System (EFS)

To use storage via the shared volume, you need to:

-   Create an EFS space from the [Elastic File System Console](https://console.aws.amazon.com/efs/home)
    
-   Check that the network configuration allows this storage to be accessed from Carbone containers
    
-   Mount a shared volume by adding the `volume` directive to the task definition:
    
    ```json
    "volume": [
    {
      "name": "template-storage",
    
      "efs_volume_configuration": {
        "file_system_id": <EFS ID>,
        "root_directory": "/app/template"
      }
    },
    {
      "name": "render-storage",
    
      "efs_volume_configuration": {
        "file_system_id": <EFS ID>,
        "root_directory": "/app/render"
      }
    }
    ]
    ```
    

### Enable Carbone options

All Carbone configuration options are available on [this page](/documentation/developer/on-premise-installation/configuration.md).

You can also create your own plugins, configure your version of LibreOffice or add your own fonts by [creating your own Carbone image](/documentation/developer/on-premise-installation/container-customization.md).

### Authentication

To enable API authentication:

-   Add the environment variable `CARBONE_AUTHENTICATION=true` to the task definition.
    
-   Generate a key pair with the same helper used on Docker and Kubernetes:
    
    ```sh
    docker run -it --platform "linux/amd64" carbone/carbone-ee:slim-5.0.0-beta.0 generate-keys
    ```
    
-   Store the public key content as a secret in AWS Secrets Manager (same as the license above), and reference it from the task definition as `CARBONE_AUTHENTICATION_PUBLIC_KEY`, in the `secrets` array.
    
-   Keep the private key secret — it never needs to leave your machine, it is only used to generate JWT tokens:
    
    ```sh
    docker run -it --platform "linux/amd64" carbone/carbone-ee:slim-5.0.0-beta.0 generate-token
    
    ## Paste in terminal content of key.pem
    ```
    

A JWT token is then displayed in the console. You can then use it in your API calls.

## Scaling and availability

Since template and render storage is external (S3 or EFS, see [Storage backends](#configuration-storage-backends) above), Carbone tasks are stateless and can be scaled horizontally:

-   Put an **Application Load Balancer** in front of the service, with a target group health check on `/status` (port `4000`).
-   Configure **ECS Service Auto Scaling** with a target-tracking policy (for example, target CPU utilization, or ALB request count per target) to adjust the desired task count automatically.
-   Because every task shares the same S3 bucket or EFS volume, there is no clustering/peer configuration to set up — any task can serve any request.

## Infrastructure as Code

### Carbone autoscaling deployment

A [complete Terraform deployment example](https://github.com/carboneio/carbone-ee-docker/tree/master/deployement/aws-ecs) is available.

## Upgrade

To upgrade Carbone on ECS:

1.  Create a new revision of the task definition with the updated `image` tag (for example `carbone/carbone-ee:full-5.8.0`).
2.  Update the service to use the new task definition revision. ECS performs a rolling deployment, starting new tasks and draining old ones behind the load balancer.

This is safe without downtime as long as template/render storage is external (S3 or EFS) rather than task-local.

## Troubleshooting

**Container logs.** Every task ships its logs to the CloudWatch log group configured in the task definition (`awslog-carbone` in the example above) — check there first for application-level errors.

**Task stuck in `PENDING` or fails to start.** Common causes:

-   Insufficient capacity for the requested `cpu`/`memory` in Fargate, or a subnet/security group that doesn't allow the task to reach the internet (to pull the image) or your VPC endpoints.

**Task starts then stops immediately.** Usually a permissions issue: the execution role needs `secretsmanager:GetSecretValue` on the license (and public key, if authentication is enabled) secret ARNs referenced in the task definition's `secrets` array.

**Health check failures on the load balancer.** Confirm the target group points at container port `4000` and path `/status`, and that the security group attached to the tasks allows inbound traffic from the load balancer.

## Related topics

- [Deploy with Docker](/documentation/developer/self-hosted-deployment/deploy-with-docker.md)
- [Deploy on Kubernetes](/documentation/developer/self-hosted-deployment/deploy-on-kubernetes.md)
- [Deploy on Google Cloud Platform](/documentation/developer/self-hosted-deployment/deploy-on-gcp.md)
- [Deploy on Azure](/documentation/developer/self-hosted-deployment/deploy-on-azure.md)
- [Deploy on AWS](/documentation/developer/self-hosted-deployment/deploy-on-aws.md)
