Deploy Frontend & Backend on AWS EC2 Using Ansible Galaxy (Step-by-Step Guide)

Ajay Avatar
Deploy Frontend & Backend on AWS EC2 Using Ansible Galaxy (Step-by-Step Guide)

 


Deploy Frontend & Backend on AWS EC2 Using Ansible Galaxy: Step-by-Step Guide

Table of Contents

  1. Introduction
  2. Prerequisites
  3. Setting Up Your Ansible Project
  4. Creating EC2 Instance Role
  5. Configuring the Frontend with Nginx
  6. Configuring the Backend with Node.js & Express
  7. Running the Full Deployment
  8. Testing & Verifying Your Deployment
  9. Troubleshooting Common Issues
  10. Important Notes About Custom Ports
  11. Conclusion

1. Introduction

Automating cloud deployments can save hours of manual work and make your infrastructure reliable and repeatable. In this tutorial, you will learn how to:

  • Launch an AWS EC2 instance using Ansible Galaxy.
  • Deploy a frontend using Nginx.
  • Deploy a backend using Node.js and Express, managed with pm2.
  • Automate everything with Ansible roles so you can reuse your setup easily.

By the end of this guide, your EC2 instance will be running both a frontend and backend service, fully automated, and ready to serve real traffic.


2. Prerequisites

Before we start, ensure you have the following:

  • An AWS account with a key pair created.
  • Ubuntu EC2 instance or knowledge to launch one via Ansible.
  • Ansible installed on your local machine.
  • AWS CLI configured on your local machine.
  • Basic knowledge of Node.js and Nginx.

3. Setting Up Your Ansible Project

First, we create a structured project folder for all roles and playbooks. A clear directory structure keeps everything organized:

ansible_project/
├── create_instance/
│   ├── tasks/main.yml
│   └── defaults/main.yml
├── frontend/
│   └── tasks/main.yml
├── backend/
│   └── tasks/main.yml
├── site.yml
└── vars.yml
  • create_instance → role to launch EC2.
  • frontend → role to configure Nginx and deploy static frontend.
  • backend → role to configure Node.js, Express, and pm2.
  • site.yml → master playbook to run all roles sequentially.
  • vars.yml → contains instance details like AMI, type, key name, and security group.

4. Creating EC2 Instance Role

Defaults (create_instance/defaults/main.yml)

instance_name: dev-ubuntu-instance
ami_id: ami-020cba7c55df1f615
instance_type: t2.micro
key_name: Ansible-1
region: us-east-1
security_group: launch-wizard-3

Tasks (create_instance/tasks/main.yml)

- name: Launch EC2 Instance
  amazon.aws.ec2_instance:
    name: "{{ instance_name }}"
    key_name: "{{ key_name }}"
    instance_type: "{{ instance_type }}"
    image_id: "{{ ami_id }}"
    region: "{{ region }}"
    security_groups: 
      - "{{ security_group }}"
    wait: yes
    count: 1
    network:
      assign_public_ip: yes
    tags:
      Name: "{{ instance_name }}"
      Environment: dev
  register: ec2

- name: Add new instance to inventory group 
  add_host: 
    name: "{{ item.public_ip_address }}"
    groups: launched
  loop: "{{ ec2.instances }}"

- name: Save dynamic inventory to file
  copy:
    dest: ./inventory.yml
    content: |
      all:
        hosts:
          webserver:
            ansible_host: {{ ec2.instances[0].public_ip_address }}
            ansible_user: ubuntu
            ansible_ssh_private_key_file: ./Ansible-1.pem

This ensures your EC2 instance is automatically registered in Ansible inventory for further configuration.


5. Configuring the Frontend with Nginx

Tasks (frontend/tasks/main.yml)

- name: Update apt repository
  apt:
    update_cache: yes

- name: Install openJDK
  apt:
    name: openjdk-11-jdk
    state: present

- name: Install Nginx
  apt:
    name: nginx
    state: present

- name: Start and enable Nginx
  service:
    name: nginx
    state: started
    enabled: yes

- name: Copy the index.html
  copy:
    src: index.html
    dest: /var/www/html/index.html
    owner: www-data
    group: www-data
    mode: '0644'

- name: Restart Nginx
  service:
    name: nginx
    state: restarted

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Ansible Roles Demo</title>
</head>
<body>
    <h2>Frontend Deployed Using Ansible</h2>
</body>
</html>

This role installs Nginx and deploys a simple frontend.


6. Configuring the Backend with Node.js & Express

Tasks (backend/tasks/main.yml)

- name: Update apt repository
  apt:
    update_cache: yes

- name: Install Node.js and npm
  apt:
    name: 
      - nodejs
      - npm
    state: present

- name: Ensure backend_app directory exists
  file:
    path: /home/ubuntu/backend_app
    state: directory
    owner: ubuntu
    group: ubuntu
    mode: '0755'

- name: Create app.js
  copy:
    dest: /home/ubuntu/backend_app/app.js
    content: |
      const express = require('express');
      const app = express();
      const port = 3000;

      app.get('/', (req, res) => {
        res.send('Hello from Node.js Backend by Ajay!');
      });

      app.listen(port, '0.0.0.0', () => {
        console.log(`Backend running at http://0.0.0.0:${port}`);
      });
    owner: ubuntu
    group: ubuntu
    mode: '0755'

- name: Initialize npm in backend_app
  command: npm init -y
  args:
    chdir: /home/ubuntu/backend_app

- name: Install express locally
  npm:
    path: /home/ubuntu/backend_app
    name: express

- name: Install pm2 globally
  npm:
    name: pm2
    global: yes

- name: Start backend using pm2
  shell: |
    pm2 delete backend || true
    pm2 start /home/ubuntu/backend_app/app.js --name backend
    pm2 save
    pm2 startup systemd -u ubuntu --hp /home/ubuntu

This ensures that Node.js backend is installed, dependencies are managed with npm and pm2 keeps the backend running even after reboot.


7. Running the Full Deployment

Once all roles are ready, run your master playbook:

ansible-playbook site.yml -i inventory.yml

This will launch your EC2, install frontend and backend, and start all services automatically.


8. Testing & Verifying

Check the frontend:

curl http://<EC2_PUBLIC_IP>

Check the backend:

pm2 list
pm2 logs backend
curl http://<EC2_PUBLIC_IP>:3000

Ensure AWS Security Group allows port 3000 if accessing backend.


9. Troubleshooting Common Issues

  • Express module not found → Run npm install express inside /home/ubuntu/backend_app.
  • Backend not starting → Check pm2 logs backend.
  • Frontend not visible → Check Nginx status: sudo systemctl status nginx.
  • Custom ports blocked → Update AWS Security Group to allow your port.

10. Important Notes About Custom Ports

  • If your backend runs on ports other than 80/443, update AWS Security Group to open TCP for that port.
  • Example for Node.js backend running on 3000:
    • Go to EC2 → Security Groups → Inbound Rules → Add Rule → Custom TCP → Port 3000
    • Source: Anywhere (0.0.0.0/0) or your IP.

Conclusion

In this guide, you learned how to:

  • Launch AWS EC2 instances automatically using Ansible.
  • Deploy frontend using Nginx and backend using Node.js & Express.
  • Automate backend service management using pm2.
  • Test and troubleshoot deployments, ensuring a production-ready environment.

By following this structure, you can extend this setup, add multiple backends, connect APIs and even integrate CI/CD pipelines.