AWS – Creating a load balancer

How to configure nginx for Joomla

AWS offers several kinds of load balancers:

  • Classic load balancer
  • Application load balancer
  • Network load balancer

We’re going to focus on the application load balancer in this section. It’s effectively an upgraded, second-generation version of the ELB service, and it offers a lot more functionality than the classic load balancer. HTTP/2 and WebSockets are supported natively, for example. The hourly rate also happens to be cheaper.

Application load balancers do not support layer-4 load balancing. For this kind of functionality, you’ll need to use a classic load balancer.

How to do it…

Follow these steps to create a load balancer using CloudFormation:

  1. Open up your text editor and create a new CloudFormation template. We’re going to require a VPC ID and some subnet IDs as Parameters. Add them to your template, as follows:
      AWSTemplateFormatVersion: '2010-09-09' 
          Type: AWS::EC2::VPC::Id 
          Description: VPC where load balancer and instance will launch 
          Type: List<AWS::EC2::Subnet::Id> 
          Description: Subnets where load balancer and instance will launch (pick at least 2)
  1. Next, we need to add some Mappings of ELB account IDs. We’d like to be able to store logs for our ELB and store them in an S3 bucket. This requires updating the bucket policy on our S3 bucket to allow access to the associated AWS Account ID. We’ll use this Mappings section to store these. Your mappings should look like this:
You can find the complete list of ELB account IDs here:
        ELBAccountID: 127311923021 
        ELBAccountID: 783225319266
  1. Now, we can start adding Resources to our template. First, we’re going to create an S3 bucket and bucket policy so that we can store our load balancer logs. To make this template portable, we’ll omit a bucket name, but, for convenience, we’ll include the bucket name in our outputs so that CloudFormation will echo the name back to us:
          Type: AWS::S3::Bucket 
          Type: AWS::S3::BucketPolicy 
              Ref: ExampleLogBucket 
                    - "s3:PutObject" 
                  Effect: "Allow" 
                      - "" 
                        - "arn:aws:s3:::" 
                        - Ref: ExampleLogBucket 
                        - "/*" 
                      Fn::FindInMap: [ ELBAccountMap, Ref: "AWS::Region",
                        ELBAccountID ]                       
  1. Next, we need to create a security group for our load balancer to reside in. This security group will allow inbound connections to port 80 (HTTP). To simplify this recipe, we’ll leave out port 443 (HTTPS), but we’ll briefly cover how to add this functionality later in this section. Since we’re adding a public load balancer, we want to allow connections to it from everywhere ( This is what our security group looks like:
        Type: AWS::EC2::SecurityGroup 
          GroupDescription: Security Group for example ELB 
              IpProtocol: tcp 
              FromPort: 80 
              ToPort: 80
  1. Now, we need to define a target group. Upon completion of this recipe, you can go ahead and register your instances in this group so that HTTP requests will be forwarded to it. Alternatively, you can attach the target group to an autoscaling group and AWS will take care of the instance registration and deregistration for you.
  1. The target group is where we specify the health checks our load balancer should perform against the target instances. This health check is necessary to determine whether a registered instance should receive traffic. The example that’s provided with this recipe includes these health check parameters, with the values all set to their defaults. Go ahead and tweak these to suit your needs, or, optionally, remove them if the defaults work for you:
        Type: AWS::ElasticLoadBalancingV2::TargetGroup 
          Port: 80 
          Protocol: HTTP 
          HealthCheckIntervalSeconds: 30 
          HealthCheckProtocol: HTTP 
          HealthCheckPort: 80 
          HealthCheckPath: / 
          HealthCheckTimeoutSeconds: 5 
          HealthyThresholdCount: 5 
          UnhealthyThresholdCount: 2 
            HttpCode: '200' 
            Ref: VPCID
  1. We need to define at least one listener, which we will add to our load balancer. A listener will listen for incoming requests to the load balancer on the port and protocol we configure for it. Requests matching the port and protocol will be forwarded to our target group.

The configuration of our listener is going to be reasonably simple. We’re listening for HTTP requests on port 80. We’re also setting up a default action for this listener, which will forward our requests to the target group we defined previously. There is a soft limit of 50 listeners per load balancer:

        Type: AWS::ElasticLoadBalancingV2::Listener 
            Ref: ExampleLoadBalancer 
            - Type: forward 
                Ref: ExampleTargetGroup 
          Port: 80 
          Protocol: HTTP
  1. Finally, now that we have all the Resources we need, we can go ahead and set up our load balancer. We’ll need to define at least two subnets for it to live in – these are included as Parameters in our example template:
        Type: AWS::ElasticLoadBalancingV2::LoadBalancer 
            - Key: access_logs.s3.enabled 
             Value: true 
            - Key: access_logs.s3.bucket 
               Ref: ExampleLogBucket 
            - Key: idle_timeout.timeout_seconds 
              Value: 60 
          Scheme: internet-facing 
            - Fn::Select: [ 0, Ref: SubnetIDs ]               
            - Fn::Select: [ 1, Ref: SubnetIDs ]               
            - Fn::GetAtt: ExampleELBSecurityGroup.GroupId
  1. Lastly, we’re going to add some Outputs to our template for convenience. We’re particularly interested in the name of the S3 bucket we created and the URL of the load balancer:
              - '' 
              - [ 'http://', { 'Fn::GetAtt': [ ExampleLoadBalancer,
                  DNSName ] }, '/' ] 
            Ref: ExampleLogBucket

Once you have created this stack and finished this recipe, don’t forget to delete the stack to avoid any future charges.

How it works…

As you can see, we’re applying a logging configuration that points to the S3 bucket we’ve created. We’re configuring this load balancer to be internet-facing, with an idle timeout of 60 seconds (the default).

All load balancers are internet-facing by default, so it’s not strictly necessary to define a Scheme in our example; however, it can be handy to include this anyway. This is especially the case if your CloudFormation template contains a mix of public and private load balancers.

If you specify a logging configuration but the load balancer can’t access the S3 bucket, your CloudFormation stack will fail to complete.

Private ELBs are not internet-facing and are only available to the resources that live inside your VPC.

There’s more…

Load balancers on AWS are highly configurable and there are many options available to you. Here are some of the more frequent ELB options you’ll encounter.


If you wish to accept HTTPS requests, you’ll need to configure an additional listener. It will look something like the following:

        Type: AWS::ElasticLoadBalancingV2::Listener 
            - CertificateArn:
            Ref: ExampleLoadBalancer 
            - Type: forward 
                Ref: ExampleTargetGroup 
          Port: 443 
          Protocol: HTTPS

The listener will need to reference a valid Amazon Resource Name (ARN) for the certificate you wish to use. It’s really easy to have AWS Certificate Manager create a certificate for you, but it does require validation of the domain name you’re generating the certificate for. You can, of course, bring your own certificate if you wish. You’ll need to import it into AWS Certificate Manager before you can use it with your ELB (or CloudFront distribution).

Unless you have specific requirements around ciphers, a good starting approach is to not define an SSL Policy and let AWS choose what is currently their best recommendation.

Path-based routing

Once you are comfortable with the ELB configuration, you can start to experiment with path-based routing. In a nutshell, it allows you to inspect a request and proxy it to different targets based on the path that’s requested.

One common scenario you might encounter is needing to route requests for /blog to a different set of servers running WordPress, instead of to your main server pool, which is running your Ruby on Rails application.

Comments are closed.