Solution that deploys WordPress containers in AWS Elastic Container Service (ECS). These containers connect to Elastic File System (EFS) to persist and share the WordPress files between them and to Relational Database Service (RDS) to store the data.
- VPC with an attached Internet gateway.
- Two sets of one public subnet and one private subnet. Each set must belong to different availability zones.
- The public subnet must route internet traffic through the VPC Internet gateway.
- The public subnet must have a NAT gateway attached.
- The private subnet must route internet traffic through the NAT gateway attached in the public subnet.
Sample network stack
AWSTemplateFormatVersion: '2010-09-09'
Description: ''
#################### STACK MAPPINGS ####################
Mappings:
SubnetConfig:
VPC:
CIDR: 10.2.0.0/16
PublicSubnet1:
CIDR: 10.2.0.0/24
PublicSubnet2:
CIDR: 10.2.1.0/24
PrivateSubnet1:
CIDR: 10.2.2.0/24
PrivateSubnet2:
CIDR: 10.2.3.0/24
#################### STACK RESOURCES ####################
Resources:
#################### VPC ####################
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !FindInMap [ SubnetConfig, VPC, CIDR ]
InstanceTenancy: default
EnableDnsHostnames: true
EnableDnsSupport: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC'
#################### INTERNET GATEWAY ####################
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-IG'
InternetGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
#################### PUBLIC ROUTE TABLE ####################
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PubRT'
DefaultPublicRoute:
DependsOn:
- InternetGatewayAttachment
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
#################### PUBLIC SUBNETS ####################
#################### SUBNET1 ####################
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !FindInMap [ SubnetConfig, PublicSubnet1, CIDR ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PubSN1'
VpcId: !Ref VPC
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet1
PublicSubnet1ElasticIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PubSN1-NG-EIP'
PublicSubnet1NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt PublicSubnet1ElasticIP.AllocationId
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PubSN1-NG'
#################### SUBNET2 ####################
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: !FindInMap [ SubnetConfig, PublicSubnet2, CIDR ]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PubSN2'
VpcId: !Ref VPC
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet2
PublicSubnet2ElasticIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PubSN2-NG-EIP'
PublicSubnet2NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt PublicSubnet2ElasticIP.AllocationId
SubnetId: !Ref PublicSubnet2
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PubSN2-NG'
#################### PRIVATE SUBNETS ####################
#################### SUBNET1 ####################
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 0, !GetAZs '' ]
CidrBlock: !FindInMap [ SubnetConfig, PrivateSubnet1, CIDR ]
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PrivSN1'
VpcId:
Ref: VPC
PrivateSubnet1RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PrivSN1-RT'
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateSubnet1RouteTable
SubnetId: !Ref PrivateSubnet1
RouteToPublicSubnet1NatGateway:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateSubnet1RouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref PublicSubnet1NatGateway
#################### SUBNET2 ####################
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select [ 1, !GetAZs '' ]
CidrBlock: !FindInMap [ SubnetConfig, PrivateSubnet2, CIDR ]
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PrivSN2'
VpcId:
Ref: VPC
PrivateSubnet2RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-VPC-PrivSN2-RT'
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateSubnet2RouteTable
SubnetId: !Ref PrivateSubnet2
RouteToPublicSubnet2NatGateway:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateSubnet2RouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref PublicSubnet2NatGateway
In case you chose to set a custom domain, you will need to create a CNAME record in the DNS configurations of your domain. The value of the record is the LoadBalancerDNSName provided in the stack outputs.
In case you want to configure WordPress as multisite, you will have to activate it by accessing the Network installation wizard: WordPress Dashboard -> Tools -> Network Setup. Follow the detailed instructions there. To access and modify the files indicated there, you have two options:
-
Mount the EFS File System on an EC2 instance to access and modify files. For this option, you can follow this tutorial.
-
Establish a connection using ECS Exec to one of the tasks running in the ECS service. For this option, you can run the following command:
aws ecs execute-command --region <REGION> --cluster <YOUR-CLUSTER-NAME> --container wordpress --command \"/bin/bash\" --interactive --task <TASK-ID>
This stack allows to create a read replica in RDS. In order for WordPress to be aware of and use this read replica, HyperDB must be installed and configured. The database endpoints are provided in the stack outputs.
To deploy the solution using AWS Console, follow to this tutorial.
To deploy the solution using AWS CLI, follow these steps:
-
Clone this repo
-
Create a file called stack.env. Below you can find a sample of the contents of this file.
VPCId=vpc-0aaa000aa00000a00 PrivateSubnetIds=subnet-11bbbbbbb11bb111b,subnet-222c2cc22c2cccc2c PublicSubnetIds=subnet-333dd3333333d3d3d,subnet-444e444444d44d44d DatabaseInstanceClass=db.t3.micro DatabaseAllocatedStorage=20 DatabaseMaxAllocatedStorage=40 DatabaseBackupRetentionPeriod=5 EnableDatabaseMultiAZ=Yes EnableDatabaseReadReplica=Yes DatabaseCredentialsRotationSchedule=30 EnableEFSAutomaticBackups=Yes EFSPerformanceMode=generalPurpose EFSThroughputMode=bursting EFSProvisionedThroughput= CustomDomain=wp.example.com CustomDomainCertificateARN=arn:aws:acm:us-east-1:111111111111:certificate/0c2189d1-b4ab-4dce-aebb-2a90b0332acd ECSTaskvCPU=.5 ECSTaskMemory=3072 ECSLogRetentionPeriod=90 ECSServiceAutoScalingMetric=AverageCPUUtilization ECSServiceAutoScalingTargetValue=80 ECSServiceAutoScalingTargetMinCapacity=1 ECSServiceAutoScalingTargetMaxCapacity=2
⚠️ Optional parameters can be left empty. -
Deploy the stack
aws cloudformation deploy --template-file ./template.yaml --capabilities CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND --parameter-overrides $(cat stack.env) --stack-name <STACK-NAME>