Jonah Uka



Step-by-Step Guide on How to Configure a Private Nginx Server in AWS using Ansible

Step-by-Step Guide on How to Configure a Private Nginx Server in AWS using Ansible

Jonah Uka's photo
Jonah Uka
·Jan 18, 2023·

10 min read

Table of contents

AWS is a cloud-based platform that offers flexible and cost-efficient computing solutions. It provides various services, such as computing power, storage options, and databases that help businesses increase their capacity and expand. With AWS, companies can quickly scale their operations and grow their businesses.

On the other hand, Ansible is a software tool that allows users to automate various tasks such as managing configurations, deploying applications, and automating other processes. With Ansible, you can automate repetitive tasks and manage complex environments with many servers and different operating systems. It is easy to use, as it does not require any agent software to be installed on the remote systems and can be run from a central location.

I completed this project as part of the AltSchool Christmas Holiday Challenge in 2022.

This article explains how to set up private servers using Nginx and Ansible on AWS, with a focus on configuring two servers for simplicity. However, the method can be scaled to accommodate more servers.

This illustration provides a general overview of the infrastructure that will be set up by the end of this tutorial. Don't worry about the terms that you're not familiar with; we'll go through them step by step.

Please refer back to the image for visual reference as we move through this tutorial.


  • An AWS account

  • A local machine, e.g a laptop

  • Git bash for Windows users

  • Some experience with Ubuntu

Getting Started

Step One: Create a VPC

What is VPC? A virtual private cloud (VPC) is a secure, isolated private cloud hosted within a public cloud.

  1. Open the Amazon VPC console at

  2. On the VPC Dashboard, click the Create VPC button.

  3. On the VPC configuration Dashboard,

    A. Choose VPC and More.

    This option automatically launches private subnets, public subnets, route tables, Internet Gateway, Availability Zones, Network Access Translator (NAT) Gateway, and others.

    Subnet: A subnet is a way to divide a larger network into smaller, more manageable sections. It's used to organize and control the flow of data within a network, and to increase security and organization.

    Route table: A route table is like a map for data packets in a network. It helps routers to decide where to send the data packets next by looking up the destination address.

    Internet Gateway: An Internet gateway is a device or service that connects a local network, such as a home or office network, to the Internet.

    Availability Zones (AZs): Availability Zones (AZs) are separate locations within a region that are designed to keep running even if something bad happens in one of the locations.

    NAT Gateway: NAT gateway is a virtual firewall that sits in between a private network and the internet.

    It's important to note that these concepts can be quite complex and have many nuances and details that have been omitted in the simplifications. It's always a good idea to consult more detailed resources and documentation to gain a deeper understanding of these concepts.

    B. Edit the Name tag for your VPC.

    C. Choose the number of Availability Zones (AZs) in which to create your NAT GateWay.

    D. Click on the Create VPC button

    The image below shows the auto-generated configurations i.e.. Subnets, Routes Tables and Network Connections.

Step Two: Launch two Private Instances

  1. Open AWS EC2 console at

  2. Click on Launch Instance

  3. On the Launch an instance page:

    A. Write a name for your servers, this is entirely up to you

    B. Select the number of instances

    C. Use Ubuntu as the Linux Distribution [for the sake of the tutorial]

    D. Generate or select a key pair.

    To access the servers we're setting up, we use key-pairs, which is a method from cryptography for safeguarding data transfers during the connection process.

    E. Give your key-pair a name

    F. Click on the Create key pair button

    What happened here was that a .pem file is downloaded to your local machine. Find out where it was downloaded to and keep it in memory, we are going to need it later in this tutorial.

    G. On the Network settings panel, click on Edit

    H. Select the VPC we created in Step One

    I. Ensure your Subnet is selected as private1 or private2

    J. Ensure that the Auto-assign public IP is set to Disable

    K. Create a new security group

    L. Edit the name of your security group

    M. Click on Launch instance

Step Three: Create a Bastion Host

A bastion host is a secure computer that acts as a gateway to a private network. In the tutorial, it is the instance that would give us access to our private instances. Configuring the bastion host is similar to the EC2 instances we configured in Step Two, except for the following:

A. Number of instances = 1

B. Select the same key-pair you generated

C. On the Network settings panel, Ensure the subnet is public1 or public2

D. Ensure that the Auto-assign public IP is set to Enable

E. Use the same security group you created in Step Two

That's all for this step.

Remember that all other steps in Step Two are the same

Step Four: Time to Connect

Remember that key pair we downloaded in Step Two? Yea, we would need it now.

Use Git Bash or any other terminal-like software

  1. Locate the key-pair file on your local machine [Hint: It has a .pem extension]

    Use ls to list the content of the directory

  2. Enter this line of code:

     chmod 400 <your_key_file.pem>

    This line of code gives the user read permission and removes all other permissions.

  3. Display the content of the <your_key_file.pem> file

     cat <your_key_file.pem>
  4. Copy the output to a simple text editor e.g. Notepad


  5. On the Amazon EC2 console, select your bastion host's instance ID

  6. Click on the Connect button

  7. Copy the "ssh -i '<your_key_file.pem>' ...." command

  8. SSH into your bastion host

  9. In your bastion host, run

     nano <your_key_file.pem>

    This opens an empty file.

  10. Copy the key-pair code into this empty file. Recall "sub-step 4"

  11. Save and exit. Ctrl + O and Ctrl + X

  12. SSH into your private instances by following the instructions in sub-steps [5 - 8]

  13. Log out of your private instances into the bastion host

Step Five: Install and Setup Ansible

Ansible allows us to do installations and configurations on several servers at the same time. But we need to install Ansible on the host machine [Bastion Host] so that we can manage the private instances.

  1. Update local package index.

     sudo apt update
  2. Install Ansible.

     sudo apt install ansible -y
  3. Create a ansible.cfg file

     nano ansible.cfg
     # Create file
  4. Edit ansible.cfg

    Inventory : A file that is used to store the ip addresses of the managed servers you want your commands to run on

     inventory = hosts
     private_key_file = ~/your_key_file.pem

    Save and exit.

    Note: host file has not been created yet. We would create it in the next step.

  5. Create your hosts file and add the ipv4 address of your private instances.

     nano hosts

    Save and exit.

  6. Create and edit the ansible playbook

    Playbooks are files that contain the commands to be run. They are written in YAML (YAML Ain't Markup Language) - a recursive acronym. Playbooks are used to automate the deployment, configuration, and management of software on remote systems. They are organized into a series of tasks, which can include things like installing software, creating users and groups, and copying files.

     nano site.yml
     - name: Provisioning Nginx web server
       hosts: all
       become: yes
         - nginx
     # nginx is a role that we have not created yet

    Save and exit.

  7. Make sure the files we just created are on the same level.

  8. Create a roles/ directory

     mkdir roles; cd roles

    Use roles to organize your playbooks into smaller, more manageable units. Roles can be reused across multiple playbooks and make it easier to share and collaborate on code. It is considered best practice to use roles in ansible. If you want to know more about it, you can check out the Ansible documentation and you can also learn about the best practices in Ansible.

  9. Create a new role directory structure with ansible-galaxy

    Remember to give it the same name as it is in the site.yml playbook, in our case, that is nginx

     ansible-galaxy init nginx; cd nginx

    If you list out the files and directories in the nginx directory, you will see a structure similar to this image.

  10. Write a bash script that would display the hostname of each private on the browser.

    cd files/; nano
    echo "<h1>This is my server $(hostname -f)</h1>" > /var/www/html/index.nginx-debian.html

    Save and Exit.

  11. Write the tasks playbook

    cd ../tasks; nano main.yml
    # tasks file for nginx
    - name: Update the servers
      command: apt update
    - name: Install Nginx
        name: nginx
    - name: Sending and execute bash script files to servers
  12. Write the handlers.

    Handlers are a type of task that are used to perform actions based on the state of other tasks. In this case, we would need to restart Nginx so that the configurations can "stick".

    cd ../handlers
    nano main.yml
    # handlers file for nginx
    - name: Restart nginx
        name: nginx
        enabled: yes
        state: restarted
  13. Run the playbook

    To run the playbook, let's go back to where we have the hosts file and site.yml playbook.

    cd ../../../
    ansible-playbook -i hosts site.yml


Step Six: Create Target Groups

We are back to the AWS EC2 Console.

  1. Scroll down on the left sidebar to find and click on Target Groups.

  2. Click on Create target group


  3. Select instances as the target type and give the target group a name.

  4. Select the VPC we created earlier.

  5. Click on next


  6. Select the Private instances in your VPC and click on include as pending below.

  7. Click on Create target group

  8. Go to the Target Groups page, Select none associated under the Loadbalancer column. Since we do not have a load balancer attached to the target group we created.

Step Seven: Create an Application Load Balancer

A load balancer evenly distributes incoming internet traffic among multiple servers to prevent overloading and improve system performance and stability.

  1. Give your Application Load Balancer a name

  2. On the Network Mapping panel, select your VPC, and choose the public subnets associated with your VPC.

  3. Create a new Security Group

  4. Give your Security Group a name, and brief description, and ensure you choose your created VPC from the drop-down menu.

  5. Edit the Inbound rules to allow HTTP and Https traffic from anywhere. Click on create and return to the previous page to assign the newly created Security Group.

    You can also do this for http and https [IPv6]


  6. On the Listeners and routing section, Choose the target group that was made earlier, and then proceed to create load balancers while keeping the rest of the settings at their default values.

Step Eight: Configure the Instance Security Group to direct to the Load Balancer

  1. Navigate to the Security Group page, select the security group associated with your instances.

  2. Edit the inbound rules to allow traffic on only the load balancer by selecting the Security Group that is associated with the load balancer. Save the rules.

  3. Confirm the health status of your instances, if it is unhealthy, restart your Nginx servers

     sudo systemctl restart nginx

    Time for magic!

  4. Go to your load balancer page and copy its DNS name, paste it on your browser and keep reloading.

    The page keeps switching between your two private instances.


This setup helps reduce the workload on the individual servers by directing traffic between servers. It can be scaled to cater to multiple servers at once, thereby reducing the risk of downtime for maintenance.

For more information about some of the concepts discussed here, check out:

You can also check out my ansible playbook for reference



Share this