Skip to content

Instantly share code, notes, and snippets.

@MrHertal
Last active April 9, 2025 10:18
Show Gist options
  • Save MrHertal/549b31a18e350b69c7200ae8d26ed691 to your computer and use it in GitHub Desktop.
Save MrHertal/549b31a18e350b69c7200ae8d26ed691 to your computer and use it in GitHub Desktop.
AWS Fargate cluster hosting Flowise exposed through ELB.
AWSTemplateFormatVersion: "2010-09-09"
Description: This template creates resources for Flowise application
Parameters:
Stage:
Description: Prefix of resource names
Type: String
Default: flowise
Mappings:
SubnetConfig:
VPC:
CIDR: "10.0.0.0/16"
PublicOne:
CIDR: "10.0.0.0/24"
PublicTwo:
CIDR: "10.0.1.0/24"
PrivateOne:
CIDR: "10.0.2.0/24"
PrivateTwo:
CIDR: "10.0.3.0/24"
Resources:
####
# VPC related resources
####
VPC:
Type: AWS::EC2::VPC
Properties:
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !FindInMap ["SubnetConfig", "VPC", "CIDR"]
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, vpc]]
PublicSubnetOne:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref "AWS::Region"
VpcId: !Ref "VPC"
CidrBlock: !FindInMap ["SubnetConfig", "PublicOne", "CIDR"]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, public-subnet-one]]
PublicSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 1
- Fn::GetAZs: !Ref "AWS::Region"
VpcId: !Ref "VPC"
CidrBlock: !FindInMap ["SubnetConfig", "PublicTwo", "CIDR"]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, public-subnet-two]]
PrivateSubnetOne:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref "AWS::Region"
VpcId: !Ref "VPC"
CidrBlock: !FindInMap ["SubnetConfig", "PrivateOne", "CIDR"]
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, private-subnet-one]]
PrivateSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 1
- Fn::GetAZs: !Ref "AWS::Region"
VpcId: !Ref "VPC"
CidrBlock: !FindInMap ["SubnetConfig", "PrivateTwo", "CIDR"]
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, private-subnet-two]]
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, igw]]
GatewayAttachement:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref "VPC"
InternetGatewayId: !Ref "InternetGateway"
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref "VPC"
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, public-rt]]
PublicRoute:
Type: AWS::EC2::Route
DependsOn: GatewayAttachement
Properties:
RouteTableId: !Ref "PublicRouteTable"
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref "InternetGateway"
PublicSubnetOneRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetOne
RouteTableId: !Ref PublicRouteTable
PublicSubnetTwoRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetTwo
RouteTableId: !Ref PublicRouteTable
NatGatewayOneAttachment:
Type: AWS::EC2::EIP
DependsOn: GatewayAttachement
Properties:
Domain: vpc
NatGatewayTwoAttachment:
Type: AWS::EC2::EIP
DependsOn: GatewayAttachement
Properties:
Domain: vpc
NatGatewayOne:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayOneAttachment.AllocationId
SubnetId: !Ref PublicSubnetOne
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, ngw-one]]
NatGatewayTwo:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayTwoAttachment.AllocationId
SubnetId: !Ref PublicSubnetTwo
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, ngw-two]]
PrivateRouteTableOne:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref "VPC"
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, private-rt-one]]
PrivateRouteOne:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableOne
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGatewayOne
PrivateRouteTableOneAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTableOne
SubnetId: !Ref PrivateSubnetOne
PrivateRouteTableTwo:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref "VPC"
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, private-rt-two]]
PrivateRouteTwo:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableTwo
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGatewayTwo
PrivateRouteTableTwoAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTableTwo
SubnetId: !Ref PrivateSubnetTwo
####
# ALB related resources
####
PublicLoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join ["-", [!Ref Stage, public-lb-sg]]
GroupDescription: Access to the public facing load balancer
VpcId: !Ref "VPC"
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
PublicLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Join ["-", [!Ref Stage, public-lb]]
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: "30"
Subnets:
- !Ref "PublicSubnetOne"
- !Ref "PublicSubnetTwo"
SecurityGroups: [!Ref "PublicLoadBalancerSG"]
# A dummy target group is used to setup the ALB to just drop traffic
# initially, before any real service target groups have been added.
DummyTargetGroupPublic:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Join ["-", [!Ref Stage, dummy-tg]]
HealthCheckIntervalSeconds: 6
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !Ref "VPC"
PublicLoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn:
- PublicLoadBalancer
Properties:
DefaultActions:
- TargetGroupArn: !Ref "DummyTargetGroupPublic"
Type: "forward"
LoadBalancerArn: !Ref "PublicLoadBalancer"
Port: 80
Protocol: HTTP
####
# ECS cluster related resources
####
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Join ["-", [!Ref Stage, ecs-cluster]]
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join ["-", [!Ref Stage, container-sg]]
GroupDescription: Access to the containers
VpcId: !Ref "VPC"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref "PublicLoadBalancerSG"
####
# EFS related resources
####
EFSFileSystemSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join ["-", [!Ref Stage, efs-sg]]
GroupDescription: Security group for EFS file system
VpcId: !Ref "VPC"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 2049
ToPort: 2049
SourceSecurityGroupId: !Ref ContainerSecurityGroup
EFSFileSystem:
Type: AWS::EFS::FileSystem
Properties:
Encrypted: true
PerformanceMode: generalPurpose
ThroughputMode: bursting
EFSMountTargetOne:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSFileSystem
SubnetId: !Ref PrivateSubnetOne
SecurityGroups:
- !Ref EFSFileSystemSecurityGroup
EFSMountTargetTwo:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSFileSystem
SubnetId: !Ref PrivateSubnetTwo
SecurityGroups:
- !Ref EFSFileSystemSecurityGroup
####
# IAM roles related resources
####
AutoScalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole"
ECSRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs.amazonaws.com]
Action: ["sts:AssumeRole"]
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action:
- "ec2:AttachNetworkInterface"
- "ec2:CreateNetworkInterface"
- "ec2:CreateNetworkInterfacePermission"
- "ec2:DeleteNetworkInterface"
- "ec2:DeleteNetworkInterfacePermission"
- "ec2:Describe*"
- "ec2:DetachNetworkInterface"
- "elasticloadbalancing:DeregisterInstancesFromLoadBalancer"
- "elasticloadbalancing:DeregisterTargets"
- "elasticloadbalancing:Describe*"
- "elasticloadbalancing:RegisterInstancesWithLoadBalancer"
- "elasticloadbalancing:RegisterTargets"
Resource: "*"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs-tasks.amazonaws.com]
Action: ["sts:AssumeRole"]
Path: /
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
# Allow the ECS Tasks to download images from ECR
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
# Allow the ECS tasks to upload logs to CloudWatch
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "*"
- Effect: Allow
Action:
- elasticfilesystem:ClientMount
- elasticfilesystem:ClientWrite
- elasticfilesystem:DescribeMountTargets
- elasticfilesystem:DescribeFileSystems
Resource: !GetAtt EFSFileSystem.Arn
####
# Flowise ECS service related resources
####
ServiceFlowise:
Type: AWS::ECS::Service
DependsOn:
- PrivateRouteOne
- PrivateRouteTwo
- ListenerRuleFlowise
Properties:
Cluster: !Ref ECSCluster
TaskDefinition: !Ref TaskDefinitionFlowise
LaunchType: FARGATE
DesiredCount: 2
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
Subnets:
- !Ref PrivateSubnetOne
- !Ref PrivateSubnetTwo
SecurityGroups:
- !Ref ContainerSecurityGroup
LoadBalancers:
- ContainerName: "flowise-service"
ContainerPort: 3000
TargetGroupArn: !Ref TargetGroupFlowise
TaskDefinitionFlowise:
Type: "AWS::ECS::TaskDefinition"
Properties:
ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
RequiresCompatibilities:
- FARGATE
ContainerDefinitions:
- Name: "flowise-service"
Image: "flowiseai/flowise:latest"
Essential: true
PortMappings:
- ContainerPort: 3000
Protocol: tcp
Environment:
- Name: Port
Value: "3000"
- Name: CORS_ORIGINS
Value: ""
- Name: IFRAME_ORIGINS
Value: ""
- Name: FLOWISE_USERNAME
Value: ""
- Name: FLOWISE_PASSWORD
Value: ""
- Name: FLOWISE_FILE_SIZE_LIMIT
Value: ""
- Name: DEBUG
Value: ""
- Name: DATABASE_PATH
Value: "/root/.flowise"
- Name: DATABASE_TYPE
Value: ""
- Name: DATABASE_PORT
Value: ""
- Name: DATABASE_HOST
Value: ""
- Name: DATABASE_NAME
Value: ""
- Name: DATABASE_USER
Value: ""
- Name: DATABASE_PASSWORD
Value: ""
- Name: DATABASE_SSL
Value: ""
- Name: DATABASE_SSL_KEY_BASE64
Value: ""
- Name: APIKEY_PATH
Value: "/root/.flowise"
- Name: SECRETKEY_PATH
Value: "/root/.flowise"
- Name: FLOWISE_SECRETKEY_OVERWRITE
Value: ""
- Name: LOG_LEVEL
Value: ""
- Name: LOG_PATH
Value: "/root/.flowise/logs"
- Name: BLOB_STORAGE_PATH
Value: "/root/.flowise/storage"
- Name: DISABLE_FLOWISE_TELEMETRY
Value: ""
EntryPoint:
- /bin/sh
- -c
- "sleep 3; flowise start"
MountPoints:
- SourceVolume: efs-volume
ContainerPath: "/root/.flowise"
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref CloudWatchLogsGroupFlowise
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: !Ref Stage
NetworkMode: awsvpc
Memory: "512"
Cpu: "256"
Volumes:
- Name: efs-volume
EFSVolumeConfiguration:
FilesystemId: !Ref EFSFileSystem
RootDirectory: /
TransitEncryption: ENABLED
CloudWatchLogsGroupFlowise:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Ref AWS::StackName
RetentionInDays: 7
TargetGroupFlowise:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref VPC
Port: 80
Protocol: HTTP
Matcher:
HttpCode: 200-299
HealthCheckIntervalSeconds: 10
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
TargetType: ip
ListenerRuleFlowise:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref PublicLoadBalancerListener
Priority: 1
Conditions:
- Field: path-pattern
Values:
- /*
Actions:
- TargetGroupArn: !Ref TargetGroupFlowise
Type: forward
Outputs:
ExternalUrl:
Description: The url of Flowise application
Value: !Sub http://${PublicLoadBalancer.DNSName}
@ciscogeek
Copy link

I could not deploy the stack and encountered below error in ECS task in AWS us-west-2 region.

05 July 2024 at 14:53 (UTC+8:00) › Error: Unexpected arguments: /bin/sh, -c, sleep 3; flowise start

@carrgust
Copy link

I had the same error. Any clue?

@MrHertal
Copy link
Author

@ciscogeek @carrgust Thanks for sharing, I've pushed a new revision with a fix.

@huiseo
Copy link

huiseo commented Sep 19, 2024

Hello,

I have forked your Gist and made some modifications to fix the 400 Bad Request errors. The issue was caused by a mismatch between the target group port and the ECS task port. The target group was configured to forward traffic to port 80, while the Flowise ECS service is listening on port 3000. This misconfiguration caused the load balancer to forward requests to the wrong port, resulting in the 400 errors. I have updated the target group configuration to forward traffic to port 3000, matching the ECS task port.

You can review the changes at the following link:
https://gist.github.com/huiseo/c6c085091c49b9b5a9700d6a0e922fda

Please take a look at the changes and, if appropriate, merge them into your original Gist.

Thank you for your time, and I appreciate your consideration.

Best regards,
huiseo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment