Back to home

Building a CI/CD Pipeline with Bitbucket Pipelines and AWS EC2 (Auto Deployment with SSH)

CI/CD Deployment Guide

Manual deployment is slow, error-prone, and not scalable.

In modern development, every code push should automatically:

✅ Update the server

✅ Restart the application

✅ Deploy without downtime

This is where CI/CD pipelines come in.

In this article, you’ll learn how to build a complete automatic deployment pipeline using:

- Bitbucket Pipelines

- SSH

- AWS EC2

- systemd

- Nginx

By the end, every push to Bitbucket will instantly deploy your code to EC2

Final CI/CD Architecture

Developer pushes code
 ↓
Bitbucket Pipeline triggers
 ↓
SSH connects to EC2
 ↓
Pulls latest code
 ↓
Restarts backend (systemd)
 ↓
Nginx serves updated app

This setup is used in real production systems.

Step 1: Prepare EC2 Server

Create Key Pair from AWS Console

Go to AWS Console → EC2 → Key Pairs

Click Create key pair

Fill:

— Name: my-ec2-key (any name)

— Key pair type: RSA

— Private key format: .pem (for Linux/Mac/Ubuntu)

Click Create key pair

👉 The .pem file will download automatically.

⚠️ Save it safely (you can’t download it again).

Connect EC2 from Local Ubuntu Terminal

Open terminal and run:

ssh -i your-ec2-key.pem ec2-user@your-ec2-public-ip

If successful, you’ll be logged into EC2 securely.

Install required tools:

sudo dnf update -y
sudo dnf install git -y
sudo dnf install nginx -y

Clone your repository:

cd /var/www
git clone https://bitbucket.org/your_username/your_repo.git ABC

Step 2: Run Backend with systemd

Create service file:

sudo nano /etc/systemd/system/ABC.service

Add:

[Unit]
Description=ABC
After=network.target
[Service]
User=ec2-user
WorkingDirectory=/var/www/ABC
ExecStart=/home/ec2-user/.local/bin/uv run python app.py
Restart=always
Environment="PATH=/home/ec2-user/.local/bin:/usr/bin"
[Install]
WantedBy=multi-user.target

Enable service:

sudo systemctl daemon-reload
sudo systemctl enable ABC.service
sudo systemctl start ABC.service

systemctl: This is the main command-line utility we use to manage services (programs that run in the background) on Linux. Think of it as a remote control for your apps and services.

daemon-reload: This is a subcommand for systemctl. When executed, it performs the following actions:

— Refreshes systemd’s memory — systemd forgets old service configurations.

— Loads new changes — if you changed a service file, it reads the new version.

— Rebuilds service order — systemd figures out which services should start first and which later.

Check status:

systemctl status ABC.service

Secure Your EC2 App with Nginx + HTTPS (Step-by-Step)

After connecting to your EC2 instance and running your backend (FastAPI) on localhost using service, the next step is to expose it securely using Nginx as a reverse proxy with HTTPS.

Open Nginx Site Configuration

mkdir -p /etc/nginx/sites-available
sudo cat /etc/nginx/sites-available/ABC

Configure Nginx for HTTPS + Reverse Proxy

# ------------------------
# HTTPS SERVER
# ------------------------

server {
    listen 443;
    listen [::]:443;
    server_name faizanarif.me www.faizanarif.me;

    # Static files
    location /static/ {
        alias /var/www/abc/static/;
    }

    # Backend (FastAPI)
    location / {
        proxy_pass http://127.0.0.1:8000;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

# ------------------------
# HTTP → HTTPS REDIRECT
# ------------------------

server {
    listen 80;
    listen [::]:80;
    server_name faizanarif.me www.faizanarif.me;

    return 301 https://$host$request_uri;
}

Create Symbolic Link

sudo ln -s /etc/nginx/sites-available/ABC /etc/nginx/sites-enabled/

This links:

sites-available/ABC → sites-enabled/ABC

Test Nginx Configuration

sudo nginx -t

You should see:

✅ syntax is ok

✅ test is successful

Join The Writer's Circle event

Reload Nginx (No Downtime)

sudo systemctl reload nginx

Now your backend:

✔ Starts automatically

✔ Restarts on crash

✔ Runs in background

Step 3: Bitbucket SSH for Automatic Deployment

Using Bitbucket SSH Key for EC2 CI/CD Deployment

Generate SSH Key in Bitbucket

1. Go to your

Bitbucket repository → Repository settings → Pipelines → SSH keys.

2. Click Generate key or Add key:

— Bitbucket provides a private key (used in Pipelines automatically) and a public key.

— The public key will go on your EC2 server.

Add Public Key to EC2

1. Connect to your EC2 instance:

ssh -i your-ec2-key.pem your-username@your-ec2-ipv4

2. Add the public key to authorized_keys:

mkdir -p ~/.ssh
nano ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Now Bitbucket can securely connect to EC2.

Step 4: Create Bitbucket Pipeline

In repository root create: bitbucket-pipelines.yml

Add:

image: python:version-name

pipelines:
  branches:
    main:
      - step:
          name: Deploy to EC2
          deployment: production
          script:
            - pipe: atlassian/ssh-run:0.6.1
              variables:
                SSH_USER: "Your_EC2_username"
                SERVER: "YOUR_EC2_PUBLIC_IPv4"
                COMMAND: |
                  cd /var/www/ABC &&
                  git pull origin main &&
                  sudo systemctl restart ABC

Step 5: Enable Pipelines

In Bitbucket:

Repository → Pipelines → Enable

That’s it! Automatic Deployment is Live

Now every time you run:

git push origin main

The pipeline will:

✔ Connect to EC2

✔ Pull latest code

✔ Restart backend

✔ Update live site

No manual SSH needed anymore 🚀

Real Production Workflow

Backend update:

git pull origin main
systemctl restart ABC.service

Nginx config update:

nginx -t
systemctl reload nginx

Best Practices

✅ Keep backend running on localhost only

✅ Use Nginx as reverse proxy

✅ Restart backend with systemd

✅ Reload nginx (no downtime)

✅ Never expose backend port publicly

Common Issues & Fixes

Permission error:

sudo chown -R username:username /var/www/ABC

SSH timeout:

✔ Check EC2 Security Group allows port 22

✔ Verify correct IP

Pipeline git pull fails first time:

Run manually on EC2:

git pull origin main

Accept SSH fingerprint.

Why This CI/CD Setup is Powerful

Without CI/CD:

❌ Manual deploy

❌ Human mistakes

❌ Downtime

❌ Slow updates

With CI/CD:

✅ Fast releases

✅ Reliable deployment

✅ Automated workflow

✅ Production ready

Final Thoughts

CI/CD is not just for big companies — even small projects benefit hugely.

Using:

• Bitbucket Pipelines

• SSH

• EC2

• systemd

• Nginx

You get a:

✔ Professional deployment system

✔ Automatic updates

✔ Scalable infrastructure

This is the foundation of modern DevOps.

Happy deploying 🚀