Skip to content
Welcome To Charanjit Cheema Blog

Welcome To Charanjit Cheema Blog

An Open Source and Cloud Blog

Menu
  • Home
  • About Me!
  • Way to my Technical Blog
  • Contact me
  • Privacy Policy
Menu

How to Deploy Docker Containers with NGINX on AWS EC2 Using Ansible and GitHub Actions

Posted on April 26, 2025 by Charanjit Singh

Have you ever wanted to automate Docker container deployment on an EC2 instance. That was exactly what I wanted to do last week and instead of doing it the old-fashioned way with SSH and manual scripts, I decided to automate it using Ansible and run it through GitHub Actions (CI/CD). The result? A super clean workflow that deploys Docker and then runs an NGINX container on an already provisioned AWS EC2 instance.

 

My Approach

I already had my EC2 instance running in AWS that I provisioned it via Terraform last time for setting up my lab on AWS then I follow the below steps to:

  1. Install Docker (in case it wasn’t already there).
  2. Pull and run an NGINX container.
  3. Configure the GitHub Secrets and Automate the deployment all using GitHub Actions.

I chose Ansible for configuration management (because it’s easy to use), and GitHub Actions to trigger the whole thing automatically on demand.

If you need help setting up your lab in AWS and understanding how to configure GitHub secrets, I recommend checking out my previous article.

In this article I walk you through step by step on how I did it by mimicking real-world setup. So, let’s start.

 

 Step 1: Set Up Your EC2

Assuming your EC2 is already up and running:

  • It’s accessible via SSH using a .pem key or a private key.
  • Port 22 is open in the security group (important for Ansible to connect).
  • You’ve noted the public IP or DNS of your instance.

In my case, I had a simple t3.nano based Ubuntu 24.04 EC2 instance in my AWS Lab for the deployment.

 

Step 2: Create Your Ansible Playbook

  • Here’s the Ansible playbook I used deploy_nginx.yml:

—

– name: Deploy Docker and run NGINX container

hosts: webserver

become: yes

 

tasks:

– name: Install Docker

apt:

name: docker.io

state: present

update_cache: yes

 

– name: Pause for 10 seconds after installing Docker

pause:

seconds: 10

 

– name: Start and enable Docker service

systemd:

name: docker

enabled: yes

state: started

 

– name: Wait for server to be ready after Docker service start

wait_for_connection:

timeout: 300

 

– name: Create a custom index.html

copy:

dest: /home/ubuntu/index.html

content: |

Hello World! I am nginx running on cj-demo-linux

 

– name: Run NGINX container with custom Page

docker_container:

name: nginx

image: nginx:latest

state: started

restart_policy: always

ports:

– “80:80”

volumes:

– /home/ubuntu/index.html:/usr/share/nginx/html/index.html:ro

 

  • Make sure your inventory.ini looks like this:

[webserver]

cj-demo-linux ansible_host=172.168.34.61 ansible_user=ubuntu ansible_ssh_private_key_file=~/.ssh/id_rsa

Replace above ec2 public ip and path to your private key accordingly.

 

Step 3: Add the Project to GitHub

Create a GitHub repo and push your files:

.github/workflows/deploy.yml

deploy_nginx.yml

inventory.ini

You can structured the code in GitHub repo like this:

├── .github

│   └── workflows

│       └── deploy.yml

├── deploy_nginx.yml

└── inventory.ini (optional I prefer to use dynamic inventory considering security, you can find the example in next steps)

You can find the code in my Public GitHub repo for more details.

 

Step 4: GitHub Actions Workflow

Here’s the GitHub action workflow or CI/CD pipeline github/workflows/deploy.yml:

name: Deploy Docker and NGINX container on EC2 via Ansible

 

on:

workflow_dispatch:

 

jobs:

deploy:

runs-on: ubuntu-latest

 

steps:

– name: Checkout code

uses: actions/checkout@v3

 

– name: Set up Python

uses: actions/setup-python@v4

with:

python-version: 3.x

 

– name: Install Ansible

run: |

python -m pip install –upgrade pip

pip install ansible

 

– name: Create SSH key

run: |

echo “${{ secrets.EC2_SSH_KEY }}” > private_key.pem

chmod 600 private_key.pem

 

# This below workflow code is optional but I recommend, use EC2_ANSIBLE_HOST as a secret to avoid hardcoding IPs and keeping your virtual machine IP secure

– name: Create Ansible inventory file by using GitHub secrets

run: |

echo “[webserver]” > inventory.ini

echo “cj-demo-linux ansible_host=${{secrets.EC2_ANSIBLE_HOST }} ansible_user=${{ secrets.EC2_USER }} ansible_ssh_private_key_file=private_key.pem ansible_ssh_common_args=’-o StrictHostKeyChecking=no -o ControlMaster=no -o ControlPersist=no -o ConnectTimeout=60′” >> inventory.ini

 

– name: Run Ansible Playbook

run: |

ansible-playbook -i inventory.ini deploy_nginx.yml

 

Secrets you’ll need:

  • EC2_SSH_KEY: Paste your private key into GitHub Secrets so that it should not get exposed.
  • EC2_USER: Provide the EC2 instance login user in GitHub Secrets which can be used for executing the ansible playbook for deployment.
  • Optional but I recommend, use EC2_ANSIBLE_HOST as a secret to avoid hardcoding IPs and keeping your virtual machine IP secure. In case if you want to use EC2_ANSIBLE_HOST variable then add the line as shown above in GitHub action workflow before task “Run Ansible Playbook”:

 

Ansible Playbook runtime optimization Tip:

These below arguments which I have mentioned in the workflow help Ansible connect without manual intervention, avoid using shared SSH sessions, ensure clean independent SSH connections for every task, and timeout cleanly if a server is slow to respond:

ansible_ssh_common_args=’-o StrictHostKeyChecking=no -o ControlMaster=no -o ControlPersist=no -o ConnectTimeout=60′

 

Step 5: Trigger the Workflow

  • Head to the Actions tab in your GitHub repo.
  • Choose the workflow and click Run workflow.

  • In less than 2 minutes, your NGINX container should be running on the EC2 instance!

To confirm, open your browser and hit your EC2’s public IP. You should see the customized NGINX working page.

 

Lessons Learned

  • I didn’t need to install any extra CI/CD tools or pipelines. GitHub Actions + Ansible was enough.
  • Keeping infra and configuration management separate gives more flexibility. EC2 from Terraform, Docker from Ansible works like a charm.
  • Using Ansible’s docker_container module simplified container deployment without writing complex shell commands.

 

What’s Next?

This approach is a strong foundation. You can extend it to:

  • Deploy custom containers.
  • Use Ansible roles for modularity.
  • Connect to ECR/ECS later for a production-grade setup.

 

Wrap Up!

This setup gave me a flexible, minimal-cost, and code-driven solution to deploy containers on AWS. If you’re juggling infra and apps like I do this approach lets you keep things clean and scalable.

Got questions or want to try this with your own app? Let’s connect or drop a comment!

Loading

  • Author
  • Recent Posts
Charanjit Singh
Follow him
Charanjit Singh
Charanjit is currently working as a Cloud Architect at Mphasis, with 18 years of experience in IT infrastructure projects, implementation, and support. While his main role is as a DevOps engineer, he holds a Cloud Architect position and has strong skills in cloud technologies and automation. His expertise includes Terraform, AWS, Azure DevOps, Azure Cloud, VMware, and Linux systems.

Charanjit is passionate about automating tasks and improving processes. He uses tools like Terraform and Azure DevOps to build and manage cloud infrastructure and streamline deployment. He also enjoys using Shell scripts and Ansible playbooks to make systems run more efficiently.

In his free time, Charanjit enjoys learning about new technologies and sharing his knowledge through his blog. When he’s not working, he likes listening to music, having a cup of coffee, and relaxing in nature.

You can connect with Charanjit on Twitter, Facebook, LinkedIn, or email him at charanjit.cheema@cjcheema.com.
Charanjit Singh
Follow him
Latest posts by Charanjit Singh (see all)
  • How to Deploy Docker Containers with NGINX on AWS EC2 Using Ansible and GitHub Actions - April 26, 2025
  • No More DynamoDB! Use Native S3 locking for Terraform State - February 7, 2025
  • How to Bring and Manage Manually Created AWS Resources Under Terraform Management - January 31, 2025

Like this:

Like Loading...

Related

Leave a ReplyCancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Tags

AWS Cloud Computing Dockers Networking Open Networking OpenSource RHEL-CentOS SDN Server Hardware SLES tcpdump Ubuntu WSL

Follow me @

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 2 other subscribers

Recent Posts

  • How to Deploy Docker Containers with NGINX on AWS EC2 Using Ansible and GitHub Actions
  • No More DynamoDB! Use Native S3 locking for Terraform State
  • How to Bring and Manage Manually Created AWS Resources Under Terraform Management
  • Iterating Cloud Resource Provisioning Using Terraform Count and For_Each Meta-Arguments
  • Terraform and Ansible Collaboration for AWS Cloud Deployment

Recent Comments

  1. Charanjit Singh on Terraform and Ansible Collaboration for AWS Cloud Deployment
  2. christinatodd2020aeaa798563 on Terraform and Ansible Collaboration for AWS Cloud Deployment
  3. Charanjit Singh on How to Set password policy in CentOS or RHEL system
  4. SAURABH on How to recover or rebuild initramfs in CentOS 7 Linux
  5. Sangita on How to Set password policy in CentOS or RHEL system

Archives

  • April 2025
  • February 2025
  • January 2025
  • August 2024
  • July 2024
  • June 2024
  • January 2024
  • August 2023
  • July 2023
  • June 2023
  • May 2023
  • September 2022
  • August 2022
  • July 2020
  • May 2020
  • February 2020
  • November 2019
  • June 2019
  • May 2019
  • March 2019
  • February 2019
  • December 2018
  • November 2018
  • October 2018
  • September 2018
  • August 2018
  • June 2018
  • May 2018
  • April 2018

Categories

  • Automation
  • Cloud Computing
  • Coding
  • CyberSecurity
  • Networking
  • OpenSource
  • RHEL-CentOS
  • Server Hardware
  • SLES
  • Technical Blog
  • Ubuntu
  • WSL

Blog Stats

  • 18,353 hits
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here: Cookie Policy
  • Home
  • About Me!
  • Way to my Technical Blog
  • Contact me
  • Privacy Policy
© 2025 Welcome To Charanjit Cheema Blog | Powered by Superbs Personal Blog theme
 

Loading Comments...
 

    %d