Back
AWS

Deploy Your Application on AWS EC2: A Step-by-Step Guide

Learn how to launch and configure an EC2 instance on AWS from scratch. We cover AMI selection, instance types, security groups, key pairs, SSH connection, Node.js installation with Nginx as a reverse proxy, process management with PM2, custom domain setup, and SSL certificates with Let's Encrypt.

Francisco ZapataWritten by Francisco Zapata
December 29, 202512 min read
Deploy Your Application on AWS EC2: A Step-by-Step Guide

Amazon Elastic Compute Cloud (EC2) is one of the most widely used services in the AWS ecosystem. It gives you the ability to launch virtual servers in the cloud with full control over the operating system, networking, and storage. In this comprehensive guide, you will learn how to deploy a Node.js application on an EC2 instance, set up Nginx as a reverse proxy, manage processes with PM2, and secure your site with a free SSL certificate from Let's Encrypt.

What Is Amazon EC2?

EC2 delivers scalable computing capacity in the AWS cloud. Instead of purchasing physical hardware, you launch instances (virtual servers) that you can configure, scale, and terminate based on demand. Each instance is built from an AMI (Amazon Machine Image), which is a template containing the operating system and pre-installed software.

Step 1: Create a Key Pair

Before launching your instance, you need a key pair for SSH access. From the EC2 console:

# You can also create it from the AWS CLI
aws ec2 create-key-pair \
  --key-name my-key-pair \
  --query "KeyMaterial" \
  --output text > my-key-pair.pem

# Set correct permissions
chmod 400 my-key-pair.pem

The .pem file is your private key. Store it in a safe location because AWS does not keep a copy.

Step 2: Configure a Security Group

Security Groups act as virtual firewalls that control inbound and outbound traffic for your instance:

# Create a security group
aws ec2 create-security-group \
  --group-name my-web-sg \
  --description "Security group for web server"

# Allow SSH (port 22)
aws ec2 authorize-security-group-ingress \
  --group-name my-web-sg \
  --protocol tcp \
  --port 22 \
  --cidr 0.0.0.0/0

# Allow HTTP (port 80)
aws ec2 authorize-security-group-ingress \
  --group-name my-web-sg \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0

# Allow HTTPS (port 443)
aws ec2 authorize-security-group-ingress \
  --group-name my-web-sg \
  --protocol tcp \
  --port 443 \
  --cidr 0.0.0.0/0
Important: In production, restrict SSH access to your IP address only instead of using 0.0.0.0/0.

Step 3: Launch the EC2 Instance

Choose an appropriate AMI and instance type:

| Instance Type | vCPUs | Memory | Recommended Use |

|---|---|---|---|

| t2.micro | 1 | 1 GB | Free Tier, testing |

| t3.small | 2 | 2 GB | Small apps |

| t3.medium | 2 | 4 GB | Medium apps |

| m5.large | 2 | 8 GB | Production |

# Launch an instance with Amazon Linux 2023
aws ec2 run-instances \
  --image-id ami-0c02fb55956c7d316 \
  --instance-type t3.small \
  --key-name my-key-pair \
  --security-groups my-web-sg \
  --count 1 \
  --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=my-web-server}]"

The t2.micro instance is eligible for the AWS Free Tier if your account is less than 12 months old.

Step 4: Connect via SSH

Once your instance is in the running state, get the public IP from the console or with the CLI:

# Get the public IP
aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=my-web-server" \
  --query "Reservations[0].Instances[0].PublicIpAddress" \
  --output text

# Connect via SSH
ssh -i my-key-pair.pem ec2-user@<PUBLIC-IP>

For Amazon Linux the default user is ec2-user, and for Ubuntu it is ubuntu.

Step 5: Install Node.js and Your Application

Inside the instance, install Node.js using nvm:

# Update the system
sudo yum update -y

# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc

# Install Node.js LTS
nvm install --lts
node --version

# Clone your application
git clone https://github.com/your-user/your-app.git
cd your-app
npm install

Step 6: Process Management with PM2

PM2 keeps your application running in the background and automatically restarts it if it crashes:

# Install PM2 globally
npm install -g pm2

# Start the application
pm2 start app.js --name "my-app"

# Configure PM2 to start on system boot
pm2 startup systemd
pm2 save

# Useful PM2 commands
pm2 list              # List processes
pm2 logs my-app       # View logs
pm2 restart my-app    # Restart
pm2 monit             # Real-time monitor

PM2 also provides load balancing with cluster mode:

# Start in cluster mode using all available CPUs
pm2 start app.js -i max --name "my-app"

Step 7: Configure Nginx as a Reverse Proxy

Nginx will receive requests on port 80/443 and forward them to your Node.js application:

# Install Nginx
sudo yum install nginx -y

# Start and enable Nginx
sudo systemctl start nginx
sudo systemctl enable nginx

Edit the Nginx configuration:

# /etc/nginx/conf.d/my-app.conf
server {
    listen 80;
    server_name your-domain.com www.your-domain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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;
        proxy_cache_bypass $http_upgrade;
    }
}
# Verify the configuration and restart
sudo nginx -t
sudo systemctl restart nginx

Step 8: Set Up Your Domain

Point your domain to your EC2 instance's public IP by creating an A record with your DNS provider. If you are using AWS Route 53:

# Create an A record in Route 53
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890 \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "your-domain.com",
        "Type": "A",
        "TTL": 300,
        "ResourceRecords": [{"Value": "3.85.XX.XX"}]
      }
    }]
  }'

Step 9: SSL with Let's Encrypt

Get a free SSL certificate using Certbot:

# Install Certbot
sudo yum install certbot python3-certbot-nginx -y

# Obtain and install the certificate
sudo certbot --nginx -d your-domain.com -d www.your-domain.com

# Verify automatic renewal
sudo certbot renew --dry-run

# Certbot automatically sets up a cron job to renew
# certificates before they expire (every 90 days)

Certbot will automatically modify your Nginx configuration to redirect HTTP to HTTPS.

Elastic IP: Static IP Address

By default, the public IP changes when you restart the instance. Assign an Elastic IP to maintain a fixed address:

# Allocate an Elastic IP
aws ec2 allocate-address --domain vpc

# Associate it with your instance
aws ec2 associate-address \
  --instance-id i-0abcd1234efgh5678 \
  --allocation-id eipalloc-0abcd1234efgh5678

Keep in mind that Elastic IPs are free while associated with a running instance. If not associated, they incur a charge.

Costs and Recommendations

EC2 charges by the hour or by the second depending on the instance type. Here are some cost-saving strategies:

  • Use Reserved Instances for predictable workloads (up to 72% discount)
  • Use Spot Instances for interrupt-tolerant workloads (up to 90% discount)
  • Set up billing alarms in CloudWatch to avoid surprises
  • Stop your instance when not in use (stop, not terminate)

Conclusion

Deploying an application on EC2 gives you full control over your infrastructure. By combining Node.js, Nginx, PM2, and Let's Encrypt, you have a robust and secure production stack. EC2 is ideal when you need custom server configuration, but consider services like Elastic Beanstalk or ECS if you prefer a higher level of infrastructure abstraction.

Comments (0)

Leave a comment

Be the first to comment