Skip to content

Instantly share code, notes, and snippets.

@HimDek
Last active April 9, 2025 03:04
Show Gist options
  • Save HimDek/ebaeac76babd871c68ebd7c4162ae1ba to your computer and use it in GitHub Desktop.
Save HimDek/ebaeac76babd871c68ebd7c4162ae1ba to your computer and use it in GitHub Desktop.
Deploy Private GitHub repo of Express, React or Django app in AWS EC2 with Domain and free SSL

Comprehensive Guide for Deploying Express, React, and Django Apps on AWS EC2 with SSL (Using Deploy Keys and PM2 for Express)

1. Launch an EC2 Instance

  1. Launch an EC2 instance on AWS with an appropriate configuration (Ubuntu, t2.micro, etc.) and ensure it is publicly accessible.
  2. Configure Security Groups:
    • Open ports 22 (SSH), 80 (HTTP), and 443 (HTTPS) to allow web traffic.

2. Connect to Your EC2 Instance

SSH into your EC2 instance:

ssh -i /path/to/your-key.pem ubuntu@<your-ec2-public-ip>

3. Install Necessary Software

  • Update your system:

    sudo apt update && sudo apt upgrade -y
  • Install Nginx:

    sudo apt install nginx -y
  • Install Certbot (for SSL certificates):

    sudo apt install certbot python3-certbot-nginx -y
  • Install Git (to clone the private repo):

    sudo apt install git -y
  • Install Node.js and PM2 (for Express):

    sudo apt install nodejs npm -y
    sudo npm install -g pm2
  • Install Python and other dependencies (for Django):

    sudo apt install python3-pip python3-dev libpq-dev nginx -y

4. Clone the Private Git Repository Using Deploy Keys

Before deploying the app, clone your private Git repository using SSH deploy keys.

Step 1: Generate a New SSH Key Pair on the EC2 Instance

  1. Generate a new SSH key pair:

    ssh-keygen -t rsa -b 4096 -C "deploy-key-for-git-repo" -f ~/.ssh/deploy_key
  2. This creates a private key (deploy_key) and a public key (deploy_key.pub) in the ~/.ssh directory.

Step 2: Add the Public Key to GitHub (or Other Git Hosting)

  1. Copy the contents of the public key:

    cat ~/.ssh/deploy_key.pub
  2. Go to your GitHub repository:

    • Navigate to SettingsDeploy KeysAdd deploy key.
    • Paste the content of the deploy_key.pub file.
    • Optionally, allow write access if required for your repo.

Step 3: Configure SSH to Use the Deploy Key

  1. Create or edit the SSH config file:

    nano ~/.ssh/config
  2. Add the following configuration to ensure SSH uses the deploy key for GitHub:

    Host github.com
        HostName github.com
        User git
        IdentityFile ~/.ssh/deploy_key
        IdentitiesOnly yes
    
  3. Ensure proper permissions on the SSH key:

    chmod 600 ~/.ssh/deploy_key

Step 4: Clone the Repository

Clone your private repository using SSH:

git clone [email protected]:<your-username>/<your-repo-name>.git

This clones your repository into the current directory.


5. Deploy Your App (Choose Your Framework)

Express (Node.js) App with PM2

  1. Navigate to your cloned Express app directory:

    cd /path/to/your/express-app
  2. Install app dependencies:

    npm install
  3. Start the app with PM2:

    pm2 start npm --name "express-app" -- start
  4. Save the PM2 process list (so PM2 can restart it automatically on reboot):

    pm2 save
  5. Configure Nginx to proxy the app (edit /etc/nginx/sites-available/default):

    server {
        listen 80;
        server_name <your-ec2-public-dns>;
    
        location / {
            proxy_pass http://localhost:3000;  # Assuming your app runs on port 3000
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
  6. Restart Nginx to apply changes:

    sudo systemctl restart nginx

React App

  1. Install Node.js:

    sudo apt install nodejs npm -y
  2. Navigate to your cloned React app directory:

    cd /path/to/your/react-app
  3. Build the React app:

    npm install
    npm run build
  4. Configure Nginx to serve the build folder:

    server {
        listen 80;
        server_name <your-ec2-public-dns>;
    
        location / {
            root /path/to/your/react-app/build;
            try_files $uri /index.html;
        }
    }
  5. Restart Nginx:

    sudo systemctl restart nginx

Django App

  1. Install Gunicorn:

    pip install gunicorn
  2. Start the Django app using Gunicorn:

    gunicorn --workers 3 --bind unix:/path/to/your/django-app/django.sock your_project_name.wsgi:application
  3. Configure Nginx to proxy Gunicorn:

    server {
        listen 80;
        server_name <your-ec2-public-dns>;
    
        location / {
            proxy_pass http://unix:/path/to/your/django-app/django.sock;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
  4. Restart Nginx:

    sudo systemctl restart nginx

6. Set Up SSL Using Certbot

  1. Ensure your EC2 instance is publicly accessible (i.e., your app should be reachable via HTTP on port 80).

  2. Obtain an SSL certificate using Certbot:

    sudo certbot --nginx -d <your-ec2-public-dns> -d www.<your-ec2-public-dns>

    Certbot will automatically:

    • Verify domain ownership.
    • Install the SSL certificate.
    • Configure Nginx to serve HTTPS traffic.
  3. Test SSL Configuration: After running Certbot, you can visit https://<your-ec2-public-dns> to verify that your site is secured with HTTPS.

  4. Renewal: Certbot automatically handles renewals. You can test the renewal process with:

    sudo certbot renew --dry-run

7. Final Nginx Configuration for HTTPS

Certbot automatically configures Nginx for HTTPS, but if needed, you can manually adjust your Nginx configuration file (/etc/nginx/sites-available/default) to ensure it's set up correctly for SSL:

server {
    listen 443 ssl;
    server_name <your-ec2-public-dns>;

    ssl_certificate /etc/letsencrypt/live/<your-ec2-public-dns>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<your-ec2-public-dns>/privkey.pem;

    # Other SSL configurations (e.g., security headers)
    location / {
        proxy_pass http://localhost:3000;  # For Express, or update based on app type
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

server {
    listen 80;
    server_name <your-ec2-public-dns>;
    return 301 https://$host$request_uri;
}

This configuration redirects all HTTP traffic to HTTPS.


8. (Optional) Configure systemd for Automatic Gunicorn Restart (For Django)

For Django apps, it’s good practice to set up systemd for automatic management of Gunicorn:

  1. Create a systemd service file for Gunicorn:

    sudo nano /etc/systemd/system/gunicorn.service
  2. Add the following configuration:

    [Unit]
    Description=gunicorn daemon for Django project
    After=network.target
    
    [Service]
    User=ubuntu
    Group=www-data
    WorkingDirectory=/path/to/your/django-app
    ExecStart=/path/to/your/virtualenv/bin/gunicorn --workers 3 --bind unix:/path/to/your/django-app/django.sock your_project_name.wsgi:application
    
    [Install]
    WantedBy=multi-user.target
  3. Start and enable the Gunicorn service:

    sudo systemctl daemon-reload
    sudo systemctl start gunicorn
    sudo systemctl enable gunicorn

9. Test Everything

After completing these steps, test your application by visiting https://<your-ec2-public-dns> in your browser. You should see your app running securely with an SSL certificate.


Conclusion

This guide covers the entire process of deploying a single app (Express with PM2, React, or Django) on an AWS EC2 instance. It includes steps for:

  • Cloning the app from a private Git repository using deploy keys.
  • Setting up PM2 to manage the Express app.
  • Setting up Nginx to serve the app.
  • Obtaining and configuring SSL certificates using Certbot.
  • Optionally setting up systemd for Django apps.

The process is designed to be clear, efficient, and secure for production deployments.

Let me know if you need more assistance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment