> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vikat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# ECS

> Deploy Vikat as a service in ECS AWS clusters

Deploy Vikat on AWS ECS using either Makefile automation or direct AWS CLI commands. This guide covers both Fargate and EC2 launch types, with options for managing configuration secrets.

<Note>
  This guide assumes you already have:

  * An ECS cluster
  * VPC with subnets
  * Security groups configured (must allow inbound traffic on port 8080 or your container port)
  * (Optional) Application Load Balancer with target group

  **Security Group Requirements:**

  * For direct access (no load balancer): Allow inbound traffic on port 8080 (or `CONTAINER_PORT`) from your IP or `0.0.0.0/0`
  * For load balancer: Allow inbound traffic from the load balancer's security group
</Note>

<Note>
  If you use PostgreSQL for `config_store` or `logs_store`, ensure the target database is UTF8 encoded. See [PostgreSQL UTF8 Requirement](../quickstart/gateway/setting-up#postgresql-utf8-requirement).
</Note>

## Deployment Methods

Choose your preferred deployment method:

<Tabs>
  <Tab title="Using Makefile">
    ## Quick Start with Makefile

    The easiest way to deploy Vikat to ECS is using the provided Makefile.

    <Note>
      **First-time deployment?** If you don't know your VPC ID or network configuration, run:

      ```bash theme={null}
      make list-ecs-network-resources
      ```

      This will list all available VPCs, subnets, and security groups in your AWS region.
    </Note>

    ```bash theme={null}
    # First, create your config.json file with your Vikat configuration
    cat > /tmp/vikat-config.json <<EOF
    {
      "config_store": {
        "enabled": true,
        "type": "postgres",
        "config": {
          "host": "your-db-host",
          "port": "5432",
          "user": "your-db-user",
          "password": "your-db-password",
          "db_name": "vikat",
          "ssl_mode": "disable"
        }
      },
      "logs_store": {
        "enabled": true,
        "type": "postgres",
        "config": {
          "host": "your-db-host",
          "port": "5432",
          "user": "your-db-user",
          "password": "your-db-password",
          "db_name": "vikat",
          "ssl_mode": "disable"
        }
      }
    }
    EOF

    # Deploy with VPC ID (recommended - auto-fetches all subnets)
    make deploy-ecs \
      VPC_ID='vpc-xxx' \
      SECURITY_GROUP_IDS='sg-xxx' \
      CONFIG_JSON_FILE='/tmp/vikat-config.json'

    # Deploy with specific subnet IDs
    make deploy-ecs \
      SUBNET_IDS='subnet-xxx,subnet-yyy' \
      SECURITY_GROUP_IDS='sg-xxx' \
      CONFIG_JSON_FILE='/tmp/vikat-config.json'

    # Deploy with EC2 launch type and SSM Parameter Store
    make deploy-ecs \
      LAUNCH_TYPE=EC2 \
      SECRET_BACKEND=ssm \
      VPC_ID='vpc-xxx' \
      SECURITY_GROUP_IDS='sg-xxx' \
      CONFIG_JSON_FILE='/tmp/vikat-config.json'

    # Deploy with Application Load Balancer
    make deploy-ecs \
      VPC_ID='vpc-xxx' \
      SECURITY_GROUP_IDS='sg-xxx' \
      TARGET_GROUP_ARN='arn:aws:elasticloadbalancing:...' \
      CONFIG_JSON_FILE='/tmp/vikat-config.json'

    # Deploy without configuration secret
    make deploy-ecs \
      VPC_ID='vpc-xxx' \
      SECURITY_GROUP_IDS='sg-xxx'
    ```

    ### Available Makefile Parameters

    | Parameter            | Default          | Description                                          |
    | -------------------- | ---------------- | ---------------------------------------------------- |
    | `ECS_CLUSTER_NAME`   | `vikat-cluster`  | Name of the ECS cluster                              |
    | `ECS_SERVICE_NAME`   | `vikat-service`  | Name of the ECS service                              |
    | `ECS_TASK_FAMILY`    | `vikat-task`     | Task definition family name                          |
    | `IMAGE_TAG`          | `latest`         | Vikat Docker image tag                               |
    | `LAUNCH_TYPE`        | `FARGATE`        | Launch type: `FARGATE` or `EC2`                      |
    | `SECRET_BACKEND`     | `secretsmanager` | Secret storage: `secretsmanager` or `ssm`            |
    | `AWS_REGION`         | `us-east-1`      | AWS region                                           |
    | `VPC_ID`             | (optional\*)     | VPC ID (auto-fetches all subnets in VPC)             |
    | `SUBNET_IDS`         | (optional\*)     | Comma-separated subnet IDs (if VPC\_ID not provided) |
    | `SECURITY_GROUP_IDS` | (required)       | Comma-separated security group IDs                   |
    | `TARGET_GROUP_ARN`   | (optional)       | ALB target group ARN                                 |
    | `CONTAINER_PORT`     | `8080`           | Container port                                       |
    | `SECRET_NAME`        | `vikat/config`   | Secret/parameter name                                |
    | `CONFIG_JSON_FILE`   | (optional)       | Path to config.json file                             |
    | `SECRET_ARN`         | (optional)       | Existing secret ARN (skip auto-lookup)               |
    | `EXECUTION_ROLE_ARN` | (optional)       | ECS task execution role ARN                          |
    | `TASK_ROLE_ARN`      | (optional)       | ECS task role ARN                                    |

    <Note>
      **Network Configuration (\*)**:\
      You must provide either `VPC_ID` OR `SUBNET_IDS`:

      * **VPC\_ID** (recommended): Automatically fetches all subnets in the VPC. Simpler and works across all availability zones.
      * **SUBNET\_IDS**: Specify exact subnet IDs if you want fine-grained control over subnet placement.
    </Note>

    ### Makefile Targets

    * `list-ecs-network-resources`: List available VPCs, subnets and security groups in your AWS region (helpful for first deployment)
    * `deploy-ecs`: Complete deployment (creates secret if CONFIG\_JSON\_FILE provided, registers task definition, creates service, waits for stabilization, and shows deployment status)
    * `create-ecs-secret`: Create/update configuration secret (requires CONFIG\_JSON\_FILE parameter)
    * `register-ecs-task-definition`: Register new task definition (with or without secret)
    * `create-ecs-service`: Create or update ECS service
    * `update-ecs-service`: Force new deployment
    * `tail-ecs-logs`: Continuously tail CloudWatch logs in real-time (Ctrl+C to exit)
    * `ecs-status`: Show current service status, running tasks, and recent logs
    * `get-ecs-url`: Get the public URL/IP to access the service (works with or without load balancer)
    * `cleanup-ecs`: Remove service and deregister task definitions

    <Note>
      **CONFIG\_JSON\_FILE Parameter**: This is optional. If provided, the Makefile will create a secret in AWS Secrets Manager or SSM Parameter Store and mount it in the ECS task. If omitted, the task will be deployed without a secret, and you can use other configuration methods (environment variables, mounted volumes, etc.).

      **How Configuration Secrets Work**: When `CONFIG_JSON_FILE` is provided, the deployment:

      1. Stores your `config.json` in AWS Secrets Manager or SSM Parameter Store
      2. Injects the secret as an environment variable `VIKAT_CONFIG` into the container
      3. Uses a custom entrypoint that:
         * Silently writes the secret content to `/data/config.json`
         * Exits with error only if `VIKAT_CONFIG` is not set
         * Then starts Vikat normally
      4. Vikat reads the configuration from the file at startup

      This approach ensures your configuration is securely stored and properly mounted as a file, which is required by Vikat. The entrypoint does not log any config data to keep logs clean and secure.
    </Note>
  </Tab>

  <Tab title="Using AWS CLI">
    ## Deployment with AWS CLI

    Deploy Vikat to ECS using direct AWS CLI commands. This section provides step-by-step instructions for both Fargate and EC2 launch types.

    <Tabs>
      <Tab title="Fargate">
        ### 1. Configuration Secret

        Choose between AWS Secrets Manager or SSM Parameter Store to store your Vikat configuration.

        <Tabs>
          <Tab title="Secrets Manager">
            Create a secret containing the Vikat configuration with Postgres backend:

            ```bash theme={null}
            # Create the configuration JSON
            cat > /tmp/vikat-config.json <<EOF
            {
              "config_store": {
                "enabled": true,
                "type": "postgres",
                "config": {
                  "host": "your-postgres-host",
                  "port": "5432",
                  "user": "your-postgres-user",
                  "password": "your-postgres-password",
                  "db_name": "vikat",
                  "ssl_mode": "disable"
                }
              },
              "logs_store": {
                "enabled": true,
                "type": "postgres",
                "config": {
                  "host": "your-postgres-host",
                  "port": "5432",
                  "user": "your-postgres-user",
                  "password": "your-postgres-password",
                  "db_name": "vikat",
                  "ssl_mode": "disable"
                }
              }
            }
            EOF

            # Create the secret
            aws secretsmanager create-secret \
              --name vikat/config \
              --secret-string file:///tmp/vikat-config.json \
              --region us-east-1

            # Get the secret ARN (save this for later)
            aws secretsmanager describe-secret \
              --secret-id vikat/config \
              --region us-east-1 \
              --query 'ARN' \
              --output text
            ```
          </Tab>

          <Tab title="SSM Parameter Store">
            Create a parameter containing the Vikat configuration:

            ```bash theme={null}
            # Create the configuration JSON
            cat > /tmp/vikat-config.json <<EOF
            {
              "config_store": {
                "enabled": true,
                "type": "postgres",
                "config": {
                  "host": "your-postgres-host",
                  "port": "5432",
                  "user": "your-postgres-user",
                  "password": "your-postgres-password",
                  "db_name": "vikat",
                  "ssl_mode": "disable"
                }
              },
              "logs_store": {
                "enabled": true,
                "type": "postgres",
                "config": {
                  "host": "your-postgres-host",
                  "port": "5432",
                  "user": "your-postgres-user",
                  "password": "your-postgres-password",
                  "db_name": "vikat",
                  "ssl_mode": "disable"
                }
              }
            }
            EOF

            # Create the parameter
            aws ssm put-parameter \
              --name /vikat/config \
              --value file:///tmp/vikat-config.json \
              --type SecureString \
              --region us-east-1

            # Get the parameter ARN (save this for later)
            aws ssm get-parameter \
              --name /vikat/config \
              --region us-east-1 \
              --query 'Parameter.ARN' \
              --output text
            ```
          </Tab>
        </Tabs>

        <Note>
          **Important**: The task definitions below include a custom `entryPoint` and `command` that:

          1. Reads the `VIKAT_CONFIG` environment variable (injected from the secret)
          2. Silently writes it to `/data/config.json` (where Vikat expects the configuration file)
          3. Exits with error if `VIKAT_CONFIG` is not set
          4. Then starts the Vikat application

          This is necessary because ECS injects secrets as environment variables, but Vikat reads configuration from a file. The entrypoint does not log any config data to keep logs clean and secure.
        </Note>

        ### 2. Task Definition

        Create a task definition for Fargate with the configuration secret injected:

        <Tabs>
          <Tab title="With Secrets Manager">
            ```bash theme={null}
            # Create task definition JSON
            cat > /tmp/vikat-task-definition.json <<EOF
            {
              "family": "vikat-task",
              "networkMode": "awsvpc",
              "requiresCompatibilities": ["FARGATE"],
              "cpu": "512",
              "memory": "1024",
              "executionRoleArn": "arn:aws:iam::YOUR_ACCOUNT_ID:role/ecsTaskExecutionRole",
              "containerDefinitions": [
                {
                  "name": "vikat",
                  "image": "vikat/vikat-gateway:latest",
                  "essential": true,
                  "entryPoint": ["/bin/sh", "-c"],
                  "command": ["if [ -n \"$VIKAT_CONFIG\" ]; then echo \"$VIKAT_CONFIG\" > /data/config.json; else echo \"ERROR: VIKAT_CONFIG not set\" >&2 && exit 1; fi && exec /app/docker-entrypoint.sh /app/main"],
                  "portMappings": [
                    {
                      "containerPort": 8080,
                      "protocol": "tcp"
                    }
                  ],
                  "secrets": [
                    {
                      "name": "VIKAT_CONFIG",
                      "valueFrom": "arn:aws:secretsmanager:us-east-1:YOUR_ACCOUNT_ID:secret:vikat/config"
                    }
                  ],
                  "healthCheck": {
                    "command": ["CMD-SHELL", "wget --no-verbose --tries=1 -O /dev/null http://127.0.0.1:8080/health || exit 1"],
                    "interval": 30,
                    "timeout": 5,
                    "retries": 3,
                    "startPeriod": 60
                  },
                  "logConfiguration": {
                    "logDriver": "awslogs",
                    "options": {
                      "awslogs-group": "/ecs/vikat-task",
                      "awslogs-region": "us-east-1",
                      "awslogs-stream-prefix": "vikat",
                      "awslogs-create-group": "true"
                    }
                  }
                }
              ]
            }
            EOF

            # Register the task definition
            aws ecs register-task-definition \
              --cli-input-json file:///tmp/vikat-task-definition.json \
              --region us-east-1
            ```

            <Note>
              The `executionRoleArn` must have permissions to:

              * Pull images from Docker Hub
              * Read secrets from Secrets Manager
              * Create CloudWatch log groups and streams
            </Note>
          </Tab>

          <Tab title="With SSM Parameter Store">
            ```bash theme={null}
            # Create task definition JSON
            cat > /tmp/vikat-task-definition.json <<EOF
            {
              "family": "vikat-task",
              "networkMode": "awsvpc",
              "requiresCompatibilities": ["FARGATE"],
              "cpu": "512",
              "memory": "1024",
              "executionRoleArn": "arn:aws:iam::YOUR_ACCOUNT_ID:role/ecsTaskExecutionRole",
              "containerDefinitions": [
                {
                  "name": "vikat",
                  "image": "vikat/vikat-gateway:latest",
                  "essential": true,
                  "entryPoint": ["/bin/sh", "-c"],
                  "command": ["if [ -n \"$VIKAT_CONFIG\" ]; then echo \"$VIKAT_CONFIG\" > /data/config.json; else echo \"ERROR: VIKAT_CONFIG not set\" >&2 && exit 1; fi && exec /app/docker-entrypoint.sh /app/main"],
                  "portMappings": [
                    {
                      "containerPort": 8080,
                      "protocol": "tcp"
                    }
                  ],
                  "secrets": [
                    {
                      "name": "VIKAT_CONFIG",
                      "valueFrom": "arn:aws:ssm:us-east-1:YOUR_ACCOUNT_ID:parameter/vikat/config"
                    }
                  ],
                  "healthCheck": {
                    "command": ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"],
                    "interval": 30,
                    "timeout": 5,
                    "retries": 3,
                    "startPeriod": 60
                  },
                  "logConfiguration": {
                    "logDriver": "awslogs",
                    "options": {
                      "awslogs-group": "/ecs/vikat-task",
                      "awslogs-region": "us-east-1",
                      "awslogs-stream-prefix": "vikat",
                      "awslogs-create-group": "true"
                    }
                  }
                }
              ]
            }
            EOF

            # Register the task definition
            aws ecs register-task-definition \
              --cli-input-json file:///tmp/vikat-task-definition.json \
              --region us-east-1
            ```

            <Note>
              The `executionRoleArn` must have permissions to:

              * Pull images from Docker Hub
              * Read parameters from SSM Parameter Store
              * Create CloudWatch log groups and streams
            </Note>
          </Tab>
        </Tabs>

        ### 3. Create ECS Service

        <Tabs>
          <Tab title="Without Load Balancer">
            ```bash theme={null}
            aws ecs create-service \
              --cluster vikat-cluster \
              --service-name vikat-service \
              --task-definition vikat-task \
              --desired-count 1 \
              --launch-type FARGATE \
              --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx,subnet-yyy],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \
              --region us-east-1
            ```
          </Tab>

          <Tab title="With Application Load Balancer">
            ```bash theme={null}
            aws ecs create-service \
              --cluster vikat-cluster \
              --service-name vikat-service \
              --task-definition vikat-task \
              --desired-count 1 \
              --launch-type FARGATE \
              --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx,subnet-yyy],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \
              --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:YOUR_ACCOUNT_ID:targetgroup/vikat-tg/xxx,containerName=vikat,containerPort=8080" \
              --health-check-grace-period-seconds 60 \
              --region us-east-1
            ```

            <Note>
              When using an ALB:

              * The security group must allow traffic from the ALB
              * The target group health check should point to `/health`
              * Set an appropriate health check grace period (60+ seconds)
            </Note>
          </Tab>
        </Tabs>

        ### 4. Update Service

        To deploy a new version or force a redeployment:

        ```bash theme={null}
        aws ecs update-service \
          --cluster vikat-cluster \
          --service vikat-service \
          --force-new-deployment \
          --region us-east-1
        ```
      </Tab>

      <Tab title="EC2">
        ### 1. Configuration Secret

        Choose between AWS Secrets Manager or SSM Parameter Store to store your Vikat configuration.

        <Tabs>
          <Tab title="Secrets Manager">
            Create a secret containing the Vikat configuration with Postgres backend:

            ```bash theme={null}
            # Create the configuration JSON
            cat > /tmp/vikat-config.json <<EOF
            {
              "config_store": {
                "enabled": true,
                "type": "postgres",
                "config": {
                  "host": "your-postgres-host",
                  "port": "5432",
                  "user": "your-postgres-user",
                  "password": "your-postgres-password",
                  "db_name": "vikat",
                  "ssl_mode": "disable"
                }
              },
              "logs_store": {
                "enabled": true,
                "type": "postgres",
                "config": {
                  "host": "your-postgres-host",
                  "port": "5432",
                  "user": "your-postgres-user",
                  "password": "your-postgres-password",
                  "db_name": "vikat",
                  "ssl_mode": "disable"
                }
              }
            }
            EOF

            # Create the secret
            aws secretsmanager create-secret \
              --name vikat/config \
              --secret-string file:///tmp/vikat-config.json \
              --region us-east-1

            # Get the secret ARN (save this for later)
            aws secretsmanager describe-secret \
              --secret-id vikat/config \
              --region us-east-1 \
              --query 'ARN' \
              --output text
            ```
          </Tab>

          <Tab title="SSM Parameter Store">
            Create a parameter containing the Vikat configuration:

            ```bash theme={null}
            # Create the configuration JSON
            cat > /tmp/vikat-config.json <<EOF
            {
              "config_store": {
                "enabled": true,
                "type": "postgres",
                "config": {
                  "host": "your-postgres-host",
                  "port": "5432",
                  "user": "your-postgres-user",
                  "password": "your-postgres-password",
                  "db_name": "vikat",
                  "ssl_mode": "disable"
                }
              },
              "logs_store": {
                "enabled": true,
                "type": "postgres",
                "config": {
                  "host": "your-postgres-host",
                  "port": "5432",
                  "user": "your-postgres-user",
                  "password": "your-postgres-password",
                  "db_name": "vikat",
                  "ssl_mode": "disable"
                }
              }
            }
            EOF

            # Create the parameter
            aws ssm put-parameter \
              --name /vikat/config \
              --value file:///tmp/vikat-config.json \
              --type SecureString \
              --region us-east-1

            # Get the parameter ARN (save this for later)
            aws ssm get-parameter \
              --name /vikat/config \
              --region us-east-1 \
              --query 'Parameter.ARN' \
              --output text
            ```
          </Tab>
        </Tabs>

        <Note>
          **Important**: The task definitions below include a custom `entryPoint` and `command` that:

          1. Reads the `VIKAT_CONFIG` environment variable (injected from the secret)
          2. Silently writes it to `/data/config.json` (where Vikat expects the configuration file)
          3. Exits with error if `VIKAT_CONFIG` is not set
          4. Then starts the Vikat application

          This is necessary because ECS injects secrets as environment variables, but Vikat reads configuration from a file. The entrypoint does not log any config data to keep logs clean and secure.
        </Note>

        ### 2. Task Definition

        Create a task definition for EC2 launch type with the configuration secret injected:

        <Tabs>
          <Tab title="With Secrets Manager">
            ```bash theme={null}
            # Create task definition JSON
            cat > /tmp/vikat-task-definition.json <<EOF
            {
              "family": "vikat-task",
              "networkMode": "awsvpc",
              "requiresCompatibilities": ["EC2"],
              "executionRoleArn": "arn:aws:iam::YOUR_ACCOUNT_ID:role/ecsTaskExecutionRole",
              "containerDefinitions": [
                {
                  "name": "vikat",
                  "image": "vikat/vikat-gateway:latest",
                  "cpu": 256,
                  "memory": 512,
                  "essential": true,
                  "entryPoint": ["/bin/sh", "-c"],
                  "command": ["if [ -n \"$VIKAT_CONFIG\" ]; then echo \"$VIKAT_CONFIG\" > /data/config.json; else echo \"ERROR: VIKAT_CONFIG not set\" >&2 && exit 1; fi && exec /app/docker-entrypoint.sh /app/main"],
                  "portMappings": [
                    {
                      "containerPort": 8080,
                      "protocol": "tcp"
                    }
                  ],
                  "secrets": [
                    {
                      "name": "VIKAT_CONFIG",
                      "valueFrom": "arn:aws:secretsmanager:us-east-1:YOUR_ACCOUNT_ID:secret:vikat/config"
                    }
                  ],
                  "healthCheck": {
                    "command": ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"],
                    "interval": 30,
                    "timeout": 5,
                    "retries": 3,
                    "startPeriod": 60
                  },
                  "logConfiguration": {
                    "logDriver": "awslogs",
                    "options": {
                      "awslogs-group": "/ecs/vikat-task",
                      "awslogs-region": "us-east-1",
                      "awslogs-stream-prefix": "vikat",
                      "awslogs-create-group": "true"
                    }
                  }
                }
              ]
            }
            EOF

            # Register the task definition
            aws ecs register-task-definition \
              --cli-input-json file:///tmp/vikat-task-definition.json \
              --region us-east-1
            ```

            <Note>
              For EC2 launch type:

              * CPU and memory are specified at the container level
              * Ensure your EC2 instances have sufficient resources
              * The ECS agent must be running on the instances
            </Note>
          </Tab>

          <Tab title="With SSM Parameter Store">
            ```bash theme={null}
            # Create task definition JSON
            cat > /tmp/vikat-task-definition.json <<EOF
            {
              "family": "vikat-task",
              "networkMode": "awsvpc",
              "requiresCompatibilities": ["EC2"],
              "executionRoleArn": "arn:aws:iam::YOUR_ACCOUNT_ID:role/ecsTaskExecutionRole",
              "containerDefinitions": [
                {
                  "name": "vikat",
                  "image": "vikat/vikat-gateway:latest",
                  "cpu": 256,
                  "memory": 512,
                  "essential": true,
                  "entryPoint": ["/bin/sh", "-c"],
                  "command": ["if [ -n \"$VIKAT_CONFIG\" ]; then echo \"$VIKAT_CONFIG\" > /data/config.json; else echo \"ERROR: VIKAT_CONFIG not set\" >&2 && exit 1; fi && exec /app/docker-entrypoint.sh /app/main"],
                  "portMappings": [
                    {
                      "containerPort": 8080,
                      "protocol": "tcp"
                    }
                  ],
                  "secrets": [
                    {
                      "name": "VIKAT_CONFIG",
                      "valueFrom": "arn:aws:ssm:us-east-1:YOUR_ACCOUNT_ID:parameter/vikat/config"
                    }
                  ],
                  "healthCheck": {
                    "command": ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1"],
                    "interval": 30,
                    "timeout": 5,
                    "retries": 3,
                    "startPeriod": 60
                  },
                  "logConfiguration": {
                    "logDriver": "awslogs",
                    "options": {
                      "awslogs-group": "/ecs/vikat-task",
                      "awslogs-region": "us-east-1",
                      "awslogs-stream-prefix": "vikat",
                      "awslogs-create-group": "true"
                    }
                  }
                }
              ]
            }
            EOF

            # Register the task definition
            aws ecs register-task-definition \
              --cli-input-json file:///tmp/vikat-task-definition.json \
              --region us-east-1
            ```
          </Tab>
        </Tabs>

        ### 3. Create ECS Service

        <Tabs>
          <Tab title="Without Load Balancer">
            ```bash theme={null}
            aws ecs create-service \
              --cluster vikat-cluster \
              --service-name vikat-service \
              --task-definition vikat-task \
              --desired-count 1 \
              --launch-type EC2 \
              --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx,subnet-yyy],securityGroups=[sg-xxx]}" \
              --region us-east-1
            ```
          </Tab>

          <Tab title="With Application Load Balancer">
            ```bash theme={null}
            aws ecs create-service \
              --cluster vikat-cluster \
              --service-name vikat-service \
              --task-definition vikat-task \
              --desired-count 1 \
              --launch-type EC2 \
              --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx,subnet-yyy],securityGroups=[sg-xxx]}" \
              --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:us-east-1:YOUR_ACCOUNT_ID:targetgroup/vikat-tg/xxx,containerName=vikat,containerPort=8080" \
              --health-check-grace-period-seconds 60 \
              --region us-east-1
            ```
          </Tab>
        </Tabs>

        ### 4. Update Service

        To deploy a new version or force a redeployment:

        ```bash theme={null}
        aws ecs update-service \
          --cluster vikat-cluster \
          --service vikat-service \
          --force-new-deployment \
          --region us-east-1
        ```
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Using CloudFormation">
    ## CloudFormation Deployment

    Deploy Vikat to ECS using AWS CloudFormation for infrastructure as code management.

    <Note>
      The CloudFormation template is available in the repository at `cloudformation/ecs-deployment.yaml`.
      You can use it directly or customize it for your needs.

      **Configuration Secret Handling**: When you provide `ConfigSecretArn`, the template automatically:

      1. Injects the secret as an environment variable `VIKAT_CONFIG` into the container
      2. Uses a custom entrypoint that:
         * Silently writes the secret content to `/data/config.json`
         * Exits with error if secret is not set
      3. This ensures Vikat can read the configuration from the expected file location

      The entrypoint does not log any config data to keep logs clean and secure.
    </Note>

    ### CloudFormation Template

    The template (`cloudformation/ecs-deployment.yaml`):

    ```yaml theme={null}
    AWSTemplateFormatVersion: '2010-09-09'
    Description: 'Deploy Vikat service on ECS'

    Parameters:
      ClusterName:
        Type: String
        Default: vikat-cluster
        Description: Name of the ECS cluster
      
      ServiceName:
        Type: String
        Default: vikat-service
        Description: Name of the ECS service
      
      TaskFamily:
        Type: String
        Default: vikat-task
        Description: Task definition family name
      
      ImageTag:
        Type: String
        Default: latest
        Description: Vikat Docker image tag
      
      LaunchType:
        Type: String
        Default: FARGATE
        AllowedValues:
          - FARGATE
          - EC2
        Description: ECS launch type
      
      ContainerPort:
        Type: Number
        Default: 8080
        Description: Container port
      
      DesiredCount:
        Type: Number
        Default: 1
        Description: Desired number of tasks
      
      VpcId:
        Type: AWS::EC2::VPC::Id
        Description: VPC ID where the service will run
      
      SubnetIds:
        Type: List<AWS::EC2::Subnet::Id>
        Description: Subnet IDs for the service (use public subnets for direct access)
      
      SecurityGroupIds:
        Type: List<AWS::EC2::SecurityGroup::Id>
        Description: Security group IDs (must allow inbound on ContainerPort)
      
      ConfigSecretArn:
        Type: String
        Default: ''
        Description: (Optional) ARN of Secrets Manager secret or SSM parameter containing config.json
      
      ExecutionRoleArn:
        Type: String
        Default: ''
        Description: (Optional) ECS task execution role ARN (will create default if not provided)
      
      TaskRoleArn:
        Type: String
        Default: ''
        Description: (Optional) ECS task role ARN
      
      TargetGroupArn:
        Type: String
        Default: ''
        Description: (Optional) ALB target group ARN for load balancing
      
      AssignPublicIp:
        Type: String
        Default: ENABLED
        AllowedValues:
          - ENABLED
          - DISABLED
        Description: Assign public IP to tasks (ENABLED for direct access without load balancer)

    Conditions:
      IsFargate: !Equals [!Ref LaunchType, FARGATE]
      HasSecret: !Not [!Equals [!Ref ConfigSecretArn, '']]
      HasExecutionRole: !Not [!Equals [!Ref ExecutionRoleArn, '']]
      HasTaskRole: !Not [!Equals [!Ref TaskRoleArn, '']]
      HasTargetGroup: !Not [!Equals [!Ref TargetGroupArn, '']]
      CreateExecutionRole: !And
        - !Not [!Condition HasExecutionRole]
        - !Condition IsFargate

    Resources:
      # CloudWatch Log Group
      LogGroup:
        Type: AWS::Logs::LogGroup
        Properties:
          LogGroupName: !Sub '/ecs/${TaskFamily}'
          RetentionInDays: 7

      # ECS Task Execution Role (created only if not provided and using Fargate)
      TaskExecutionRole:
        Type: AWS::IAM::Role
        Condition: CreateExecutionRole
        Properties:
          RoleName: !Sub '${ServiceName}-execution-role'
          AssumeRolePolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Principal:
                  Service: ecs-tasks.amazonaws.com
                Action: sts:AssumeRole
          ManagedPolicyArns:
            - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
          Policies:
            - PolicyName: SecretAccess
              PolicyDocument:
                Version: '2012-10-17'
                Statement:
                  - Effect: Allow
                    Action:
                      - secretsmanager:GetSecretValue
                      - ssm:GetParameter
                      - ssm:GetParameters
                    Resource:
                      - !Sub 'arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:vikat/*'
                      - !Sub 'arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/vikat/*'
                  - Effect: Allow
                    Action:
                      - kms:Decrypt
                    Resource: '*'

      # ECS Task Definition
      TaskDefinition:
        Type: AWS::ECS::TaskDefinition
        Properties:
          Family: !Ref TaskFamily
          NetworkMode: awsvpc
          RequiresCompatibilities:
            - !Ref LaunchType
          Cpu: !If [IsFargate, '512', '256']
          Memory: !If [IsFargate, '1024', '512']
          ExecutionRoleArn: !If
            - HasExecutionRole
            - !Ref ExecutionRoleArn
            - !If
              - CreateExecutionRole
              - !GetAtt TaskExecutionRole.Arn
              - !Ref AWS::NoValue
          TaskRoleArn: !If [HasTaskRole, !Ref TaskRoleArn, !Ref AWS::NoValue]
          ContainerDefinitions:
            - Name: vikat
              Image: !Sub 'vikat/vikat-gateway:${ImageTag}'
              Essential: true
              EntryPoint: !If
                - HasSecret
                - - /bin/sh
                  - -c
                - !Ref AWS::NoValue
              Command: !If
                - HasSecret
                - - 'if [ -n "$VIKAT_CONFIG" ]; then echo "$VIKAT_CONFIG" > /data/config.json; else echo "ERROR: VIKAT_CONFIG not set" >&2 && exit 1; fi && exec /app/docker-entrypoint.sh /app/main'
                - !Ref AWS::NoValue
              PortMappings:
                - ContainerPort: !Ref ContainerPort
                  Protocol: tcp
              Environment: []
              Secrets: !If
                - HasSecret
                - - Name: VIKAT_CONFIG
                    ValueFrom: !Ref ConfigSecretArn
                - !Ref AWS::NoValue
              HealthCheck:
                Command:
                  - CMD-SHELL
                  - !Sub 'wget --no-verbose --tries=1 --spider http://localhost:${ContainerPort}/health || exit 1'
                Interval: 30
                Timeout: 5
                Retries: 3
                StartPeriod: 60
              LogConfiguration:
                LogDriver: awslogs
                Options:
                  awslogs-group: !Ref LogGroup
                  awslogs-region: !Ref AWS::Region
                  awslogs-stream-prefix: vikat

      # ECS Service
      Service:
        Type: AWS::ECS::Service
        Properties:
          ServiceName: !Ref ServiceName
          Cluster: !Ref ClusterName
          TaskDefinition: !Ref TaskDefinition
          DesiredCount: !Ref DesiredCount
          LaunchType: !Ref LaunchType
          NetworkConfiguration:
            AwsvpcConfiguration:
              Subnets: !Ref SubnetIds
              SecurityGroups: !Ref SecurityGroupIds
              AssignPublicIp: !Ref AssignPublicIp
          LoadBalancers: !If
            - HasTargetGroup
            - - ContainerName: vikat
                ContainerPort: !Ref ContainerPort
                TargetGroupArn: !Ref TargetGroupArn
            - !Ref AWS::NoValue
          HealthCheckGracePeriodSeconds: !If [HasTargetGroup, 60, !Ref AWS::NoValue]

    Outputs:
      ServiceName:
        Description: ECS Service Name
        Value: !Ref Service
        Export:
          Name: !Sub '${AWS::StackName}-ServiceName'
      
      TaskDefinitionArn:
        Description: Task Definition ARN
        Value: !Ref TaskDefinition
        Export:
          Name: !Sub '${AWS::StackName}-TaskDefinitionArn'
      
      LogGroupName:
        Description: CloudWatch Log Group
        Value: !Ref LogGroup
        Export:
          Name: !Sub '${AWS::StackName}-LogGroupName'
      
      ExecutionRoleArn:
        Condition: CreateExecutionRole
        Description: Created Task Execution Role ARN
        Value: !GetAtt TaskExecutionRole.Arn
        Export:
          Name: !Sub '${AWS::StackName}-ExecutionRoleArn'
    ```

    ### Deploy with CloudFormation

    <Tabs>
      <Tab title="Without Load Balancer">
        **Deploy without configuration secret:**

        ```bash theme={null}
        aws cloudformation create-stack \
          --stack-name vikat-ecs-stack \
          --template-body file://cloudformation/ecs-deployment.yaml \
          --parameters \
            ParameterKey=VpcId,ParameterValue=vpc-xxx \
            ParameterKey=SubnetIds,ParameterValue="subnet-xxx\,subnet-yyy" \
            ParameterKey=SecurityGroupIds,ParameterValue="sg-xxx" \
          --capabilities CAPABILITY_NAMED_IAM \
          --region us-east-1

        # Wait for stack creation
        aws cloudformation wait stack-create-complete \
          --stack-name vikat-ecs-stack \
          --region us-east-1

        # Get service details
        aws cloudformation describe-stacks \
          --stack-name vikat-ecs-stack \
          --region us-east-1 \
          --query 'Stacks[0].Outputs'
        ```

        **Deploy with Secrets Manager:**

        First, create the secret:

        ```bash theme={null}
        aws secretsmanager create-secret \
          --name vikat/config \
          --secret-string file://config.json \
          --region us-east-1

        # Get the secret ARN
        SECRET_ARN=$(aws secretsmanager describe-secret \
          --secret-id vikat/config \
          --region us-east-1 \
          --query 'ARN' \
          --output text)
        ```

        Then deploy with the secret:

        ```bash theme={null}
        aws cloudformation create-stack \
          --stack-name vikat-ecs-stack \
          --template-body file://cloudformation/ecs-deployment.yaml \
          --parameters \
            ParameterKey=VpcId,ParameterValue=vpc-xxx \
            ParameterKey=SubnetIds,ParameterValue="subnet-xxx\,subnet-yyy" \
            ParameterKey=SecurityGroupIds,ParameterValue="sg-xxx" \
            ParameterKey=ConfigSecretArn,ParameterValue=$SECRET_ARN \
          --capabilities CAPABILITY_NAMED_IAM \
          --region us-east-1
        ```
      </Tab>

      <Tab title="With Load Balancer">
        ```bash theme={null}
        aws cloudformation create-stack \
          --stack-name vikat-ecs-stack \
          --template-body file://cloudformation/ecs-deployment.yaml \
          --parameters \
            ParameterKey=VpcId,ParameterValue=vpc-xxx \
            ParameterKey=SubnetIds,ParameterValue="subnet-xxx\,subnet-yyy" \
            ParameterKey=SecurityGroupIds,ParameterValue="sg-xxx" \
            ParameterKey=TargetGroupArn,ParameterValue=arn:aws:elasticloadbalancing:... \
            ParameterKey=AssignPublicIp,ParameterValue=DISABLED \
          --capabilities CAPABILITY_NAMED_IAM \
          --region us-east-1
        ```

        <Note>
          When using a load balancer, you can set `AssignPublicIp=DISABLED` if your tasks don't need direct internet access (they'll use NAT Gateway via the load balancer).
        </Note>
      </Tab>

      <Tab title="EC2 Launch Type">
        ```bash theme={null}
        aws cloudformation create-stack \
          --stack-name vikat-ecs-stack \
          --template-body file://cloudformation/ecs-deployment.yaml \
          --parameters \
            ParameterKey=VpcId,ParameterValue=vpc-xxx \
            ParameterKey=SubnetIds,ParameterValue="subnet-xxx\,subnet-yyy" \
            ParameterKey=SecurityGroupIds,ParameterValue="sg-xxx" \
            ParameterKey=LaunchType,ParameterValue=EC2 \
            ParameterKey=ExecutionRoleArn,ParameterValue=arn:aws:iam::ACCOUNT:role/ecsTaskExecutionRole \
          --capabilities CAPABILITY_NAMED_IAM \
          --region us-east-1
        ```

        <Note>
          For EC2 launch type, you must provide an existing `ExecutionRoleArn` as the template only auto-creates roles for Fargate.
        </Note>
      </Tab>
    </Tabs>

    ### Update Stack

    To update your deployment (e.g., change image tag or configuration):

    ```bash theme={null}
    # Update the stack
    aws cloudformation update-stack \
      --stack-name vikat-ecs-stack \
      --template-body file://cloudformation/ecs-deployment.yaml \
      --parameters \
        ParameterKey=VpcId,UsePreviousValue=true \
        ParameterKey=SubnetIds,UsePreviousValue=true \
        ParameterKey=SecurityGroupIds,UsePreviousValue=true \
        ParameterKey=ImageTag,ParameterValue=v1.2.0 \
      --capabilities CAPABILITY_NAMED_IAM \
      --region us-east-1

    # Wait for update to complete
    aws cloudformation wait stack-update-complete \
      --stack-name vikat-ecs-stack \
      --region us-east-1
    ```

    ### Get Service URL

    After deployment, get your service URL:

    ```bash theme={null}
    # Get the task public IP (without load balancer)
    TASK_ARN=$(aws ecs list-tasks \
      --cluster vikat-cluster \
      --service-name vikat-service \
      --region us-east-1 \
      --query 'taskArns[0]' \
      --output text)

    ENI_ID=$(aws ecs describe-tasks \
      --cluster vikat-cluster \
      --tasks $TASK_ARN \
      --region us-east-1 \
      --query 'tasks[0].attachments[0].details[?name==`networkInterfaceId`].value' \
      --output text)

    PUBLIC_IP=$(aws ec2 describe-network-interfaces \
      --network-interface-ids $ENI_ID \
      --region us-east-1 \
      --query 'NetworkInterfaces[0].Association.PublicIp' \
      --output text)

    echo "Service URL: http://$PUBLIC_IP:8080"
    echo "Health check: http://$PUBLIC_IP:8080/health"

    # Test the service
    curl http://$PUBLIC_IP:8080/health
    ```

    ### Monitor Logs

    ```bash theme={null}
    # Tail logs
    aws logs tail /ecs/vikat-task --follow --region us-east-1

    # View recent logs
    LOG_STREAM=$(aws logs describe-log-streams \
      --log-group-name /ecs/vikat-task \
      --order-by LastEventTime \
      --descending \
      --max-items 1 \
      --region us-east-1 \
      --query 'logStreams[0].logStreamName' \
      --output text)

    aws logs get-log-events \
      --log-group-name /ecs/vikat-task \
      --log-stream-name $LOG_STREAM \
      --region us-east-1
    ```

    ### Delete Stack

    To remove all resources:

    ```bash theme={null}
    aws cloudformation delete-stack \
      --stack-name vikat-ecs-stack \
      --region us-east-1

    # Wait for deletion
    aws cloudformation wait stack-delete-complete \
      --stack-name vikat-ecs-stack \
      --region us-east-1
    ```

    ### CloudFormation Parameters Reference

    | Parameter          | Default         | Required | Description                        |
    | ------------------ | --------------- | -------- | ---------------------------------- |
    | `ClusterName`      | `vikat-cluster` | No       | ECS cluster name (must exist)      |
    | `ServiceName`      | `vikat-service` | No       | ECS service name                   |
    | `TaskFamily`       | `vikat-task`    | No       | Task definition family             |
    | `ImageTag`         | `latest`        | No       | Docker image tag                   |
    | `LaunchType`       | `FARGATE`       | No       | `FARGATE` or `EC2`                 |
    | `ContainerPort`    | `8080`          | No       | Container port                     |
    | `DesiredCount`     | `1`             | No       | Number of tasks                    |
    | `VpcId`            | -               | **Yes**  | VPC ID                             |
    | `SubnetIds`        | -               | **Yes**  | Comma-separated subnet IDs         |
    | `SecurityGroupIds` | -               | **Yes**  | Comma-separated security group IDs |
    | `ConfigSecretArn`  | (empty)         | No       | Secret/parameter ARN               |
    | `ExecutionRoleArn` | (empty)         | No       | Task execution role ARN            |
    | `TaskRoleArn`      | (empty)         | No       | Task role ARN                      |
    | `TargetGroupArn`   | (empty)         | No       | ALB target group ARN               |
    | `AssignPublicIp`   | `ENABLED`       | No       | Assign public IP to tasks          |
  </Tab>
</Tabs>

## IAM Permissions

### Task Execution Role

The task execution role (`ecsTaskExecutionRole`) needs the following permissions:

<Note>
  The Makefile automatically creates the CloudWatch log group `/ecs/vikat-task`, so the execution role only needs `CreateLogStream` and `PutLogEvents` permissions, not `CreateLogGroup`.
</Note>

<Tabs>
  <Tab title="For Secrets Manager">
    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "ecr:GetAuthorizationToken",
            "ecr:BatchCheckLayerAvailability",
            "ecr:GetDownloadUrlForLayer",
            "ecr:BatchGetImage"
          ],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ],
          "Resource": "arn:aws:logs:*:*:log-group:/ecs/vikat-task:*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "secretsmanager:GetSecretValue"
          ],
          "Resource": "arn:aws:secretsmanager:us-east-1:YOUR_ACCOUNT_ID:secret:vikat/config*"
        }
      ]
    }
    ```
  </Tab>

  <Tab title="For SSM Parameter Store">
    ```json theme={null}
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "ecr:GetAuthorizationToken",
            "ecr:BatchCheckLayerAvailability",
            "ecr:GetDownloadUrlForLayer",
            "ecr:BatchGetImage"
          ],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "logs:CreateLogStream",
            "logs:PutLogEvents"
          ],
          "Resource": "arn:aws:logs:*:*:log-group:/ecs/vikat-task:*"
        },
        {
          "Effect": "Allow",
          "Action": [
            "ssm:GetParameters",
            "ssm:GetParameter"
          ],
          "Resource": "arn:aws:ssm:us-east-1:YOUR_ACCOUNT_ID:parameter/vikat/config"
        },
        {
          "Effect": "Allow",
          "Action": [
            "kms:Decrypt"
          ],
          "Resource": "arn:aws:kms:us-east-1:YOUR_ACCOUNT_ID:key/YOUR_KMS_KEY_ID"
        }
      ]
    }
    ```
  </Tab>
</Tabs>

## Accessing Your Service

### Without Load Balancer

When deployed without a load balancer, the ECS task gets a public IP address. You can find it using AWS CLI:

```bash theme={null}
# Get the public IP address of your running task
aws ec2 describe-network-interfaces \
  --network-interface-ids $(aws ecs describe-tasks \
    --cluster vikat-cluster \
    --tasks $(aws ecs list-tasks \
      --cluster vikat-cluster \
      --service-name vikat-service \
      --region us-east-1 \
      --query 'taskArns[0]' \
      --output text) \
    --region us-east-1 \
    --query 'tasks[0].attachments[0].details[?name==`networkInterfaceId`].value' \
    --output text) \
  --region us-east-1 \
  --query 'NetworkInterfaces[0].Association.PublicIp' \
  --output text
```

<Warning>
  **Important Notes:**

  * The public IP changes every time the task is restarted
  * You must allow inbound traffic on port 8080 (or your `CONTAINER_PORT`) in your security group
  * For production, consider using an Application Load Balancer for a stable endpoint
</Warning>

**Testing your deployment:**

```bash theme={null}
# Test health endpoint (replace YOUR_PUBLIC_IP with the IP from above)
curl http://YOUR_PUBLIC_IP:8080/health

# Expected response
{"status":"ok"}
```

### With Load Balancer

If you deployed with `TARGET_GROUP_ARN`, your service is accessible via the load balancer's DNS name:

```bash theme={null}
# Get the load balancer DNS name (replace YOUR_TARGET_GROUP_ARN with your actual ARN)
aws elbv2 describe-load-balancers \
  --load-balancer-arns $(aws elbv2 describe-target-groups \
    --target-group-arns YOUR_TARGET_GROUP_ARN \
    --region us-east-1 \
    --query 'TargetGroups[0].LoadBalancerArns[0]' \
    --output text) \
  --region us-east-1 \
  --query 'LoadBalancers[0].DNSName' \
  --output text

# Test via load balancer (replace YOUR_ALB_DNS with the DNS from above)
curl http://YOUR_ALB_DNS/health
```

The load balancer provides:

* ✅ Stable DNS endpoint
* ✅ SSL/TLS termination (if configured)
* ✅ Health checks with automatic failover
* ✅ Multiple task load balancing

## Monitoring and Logs

### Tail Logs (Makefile)

The easiest way to monitor your deployment logs:

```bash theme={null}
# Tail logs in real-time (press Ctrl+C to exit)
make tail-ecs-logs

# Check service status and recent logs
make ecs-status
```

<Note>
  The `deploy-ecs` command automatically waits for the deployment to stabilize and shows you:

  * Deployment status (running/desired count)
  * Task details (ARN, status, health)
  * Recent logs (last 20 events)

  After deployment completes, use `make tail-ecs-logs` to continuously monitor your application.
</Note>

### View Logs (AWS CLI)

```bash theme={null}
# Tail logs using AWS CLI v2 (recommended)
aws logs tail /ecs/vikat-task --follow --region us-east-1

# Get log stream names
aws logs describe-log-streams \
  --log-group-name /ecs/vikat-task \
  --order-by LastEventTime \
  --descending \
  --max-items 5 \
  --region us-east-1

# View logs from a specific stream
aws logs get-log-events \
  --log-group-name /ecs/vikat-task \
  --log-stream-name vikat/vikat/TASK_ID \
  --region us-east-1
```

### Check Service Status

```bash theme={null}
# Describe service
aws ecs describe-services \
  --cluster vikat-cluster \
  --services vikat-service \
  --region us-east-1

# List tasks
aws ecs list-tasks \
  --cluster vikat-cluster \
  --service-name vikat-service \
  --region us-east-1

# Describe task
aws ecs describe-tasks \
  --cluster vikat-cluster \
  --tasks TASK_ARN \
  --region us-east-1
```

## Cleanup

To remove all ECS resources:

```bash theme={null}
# Using Makefile
make cleanup-ecs

# Or manually
# Delete service
aws ecs update-service \
  --cluster vikat-cluster \
  --service vikat-service \
  --desired-count 0 \
  --region us-east-1

aws ecs delete-service \
  --cluster vikat-cluster \
  --service vikat-service \
  --region us-east-1

# Deregister task definitions
aws ecs list-task-definitions \
  --family-prefix vikat-task \
  --region us-east-1 \
  --query 'taskDefinitionArns[]' \
  --output text | \
  xargs -n 1 aws ecs deregister-task-definition --task-definition --region us-east-1

# Delete secret (optional)
aws secretsmanager delete-secret \
  --secret-id vikat/config \
  --force-delete-without-recovery \
  --region us-east-1

# Or delete SSM parameter (optional)
aws ssm delete-parameter \
  --name /vikat/config \
  --region us-east-1
```
