📦
Bunty

Deploy to AWS

Deploy your Bunty application to AWS using various services like EC2, ECS, Elastic Beanstalk, or Lambda.

Deployment Options

1. EC2 (Virtual Machines)

2. ECS/Fargate (Containers)

3. Elastic Beanstalk (Platform as a Service)

4. Lambda (Serverless)

5. EKS (Kubernetes)

Option 1: AWS EC2

Setup EC2 Instance

# Launch EC2 instance (Ubuntu 22.04 LTS)
# Choose t3.small or larger
# Configure security group: Allow ports 22, 80, 443, 3000

Connect and Setup

# SSH into instance
ssh -i your-key.pem ubuntu@your-ec2-ip

# Update system
sudo apt update && sudo apt upgrade -y

# Install Bun
curl -fsSL https://bun.sh/install | bash
source ~/.bashrc

# Install Node.js (if needed)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# Install PM2
sudo npm install -g pm2

Deploy Application

# Clone repository
git clone https://github.com/yourusername/your-bunty-app.git
cd your-bunty-app

# Install dependencies
bun install --production

# Set environment variables
sudo nano /etc/environment
# Add: NODE_ENV=production, DATABASE_URL, etc.

# Start with PM2
pm2 start ecosystem.config.js --env production

# Save PM2 config
pm2 save
pm2 startup

Setup Nginx Reverse Proxy

# Install Nginx
sudo apt install nginx -y

# Configure
sudo nano /etc/nginx/sites-available/bunty-app
server {
    listen 80;
    server_name 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;
    }
}
# Enable site
sudo ln -s /etc/nginx/sites-available/bunty-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

# Install SSL with Let's Encrypt
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d your-domain.com

Option 2: AWS ECS (Fargate)

Create Dockerfile

FROM oven/bun:1-alpine
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile --production
COPY . .
EXPOSE 3000
CMD ["bun", "run", "src/main.ts"]

Push to ECR

# Create ECR repository
aws ecr create-repository --repository-name bunty-app

# Login to ECR
aws ecr get-login-password --region us-east-1 | \
  docker login --username AWS --password-stdin \
  <account-id>.dkr.ecr.us-east-1.amazonaws.com

# Build and tag
docker build -t bunty-app .
docker tag bunty-app:latest \
  <account-id>.dkr.ecr.us-east-1.amazonaws.com/bunty-app:latest

# Push
docker push <account-id>.dkr.ecr.us-east-1.amazonaws.com/bunty-app:latest

Create ECS Task Definition

{
  "family": "bunty-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "512",
  "memory": "1024",
  "executionRoleArn": "arn:aws:iam::<account>:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "bunty-app",
      "image": "<account>.dkr.ecr.us-east-1.amazonaws.com/bunty-app:latest",
      "portMappings": [
        {
          "containerPort": 3000,
          "protocol": "tcp"
        }
      ],
      "environment": [
        {
          "name": "NODE_ENV",
          "value": "production"
        }
      ],
      "secrets": [
        {
          "name": "DATABASE_URL",
          "valueFrom": "arn:aws:secretsmanager:region:account:secret:db-url"
        }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/bunty-app",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      },
      "healthCheck": {
        "command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
        "interval": 30,
        "timeout": 5,
        "retries": 3
      }
    }
  ]
}

Create ECS Service

# Create cluster
aws ecs create-cluster --cluster-name bunty-cluster

# Create service
aws ecs create-service \
  --cluster bunty-cluster \
  --service-name bunty-service \
  --task-definition bunty-app \
  --desired-count 2 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-xxx],securityGroups=[sg-xxx],assignPublicIp=ENABLED}" \
  --load-balancers "targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=bunty-app,containerPort=3000"

Application Load Balancer

# Create ALB
aws elbv2 create-load-balancer \
  --name bunty-alb \
  --subnets subnet-xxx subnet-yyy \
  --security-groups sg-xxx

# Create target group
aws elbv2 create-target-group \
  --name bunty-targets \
  --protocol HTTP \
  --port 3000 \
  --vpc-id vpc-xxx \
  --target-type ip \
  --health-check-path /health

# Create listener
aws elbv2 create-listener \
  --load-balancer-arn arn:aws:elasticloadbalancing:... \
  --protocol HTTP \
  --port 80 \
  --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:...

Option 3: Elastic Beanstalk

Install EB CLI

pip install awsebcli

Initialize

# Initialize EB application
eb init -p docker bunty-app --region us-east-1

# Create environment
eb create bunty-prod \
  --instance-type t3.small \
  --envvars NODE_ENV=production,PORT=3000

Deploy

# Deploy
eb deploy

# Open in browser
eb open

# View logs
eb logs

# SSH into instance
eb ssh

Configuration File

Create .ebextensions/environment.config:

option_settings:
  aws:elasticbeanstalk:application:environment:
    NODE_ENV: production
    PORT: 3000
  aws:autoscaling:launchconfiguration:
    InstanceType: t3.small
    IamInstanceProfile: aws-elasticbeanstalk-ec2-role
  aws:autoscaling:asg:
    MinSize: 2
    MaxSize: 6
  aws:elbv2:listener:443:
    Protocol: HTTPS
    SSLCertificateArns: arn:aws:acm:region:account:certificate/xxx

Option 4: AWS Lambda

Create Handler

// src/lambda.ts
import { BuntyApplication } from '@bunty/common';
import { AppModule } from './app.module';
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';

let app: any;

export const handler = async (
  event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
  if (!app) {
    app = await BuntyApplication.create({
      module: AppModule,
    });
  }

  const response = await app.handleLambda(event);
  return response;
};

Package for Lambda

# Install dependencies
bun install --production

# Create deployment package
zip -r function.zip . -x "*.git*" "node_modules/.cache/*"

# Or use esbuild for smaller bundle
bunx esbuild src/lambda.ts \
  --bundle \
  --platform=node \
  --target=node20 \
  --outfile=dist/index.js

Deploy with SAM

Create template.yaml:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
  BuntyFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: bunty-app
      Runtime: nodejs20.x
      Handler: index.handler
      CodeUri: ./dist
      MemorySize: 512
      Timeout: 30
      Environment:
        Variables:
          NODE_ENV: production
          DATABASE_URL: !Ref DatabaseUrl
      Events:
        ApiEvent:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY

  DatabaseUrl:
    Type: AWS::SSM::Parameter
    Properties:
      Name: /bunty/database-url
      Type: String
      Value: postgresql://...

Deploy:

sam build
sam deploy --guided

Database: RDS

Create RDS Instance

aws rds create-db-instance \
  --db-instance-identifier bunty-db \
  --db-instance-class db.t3.micro \
  --engine postgres \
  --master-username admin \
  --master-user-password YourPassword123 \
  --allocated-storage 20 \
  --vpc-security-group-ids sg-xxx \
  --publicly-accessible

Connection String

postgresql://admin:YourPassword123@bunty-db.xxxxx.us-east-1.rds.amazonaws.com:5432/bunty

Cache: ElastiCache

Create Redis Cluster

aws elasticache create-cache-cluster \
  --cache-cluster-id bunty-cache \
  --cache-node-type cache.t3.micro \
  --engine redis \
  --num-cache-nodes 1 \
  --security-group-ids sg-xxx

Storage: S3

Create Bucket

aws s3 mb s3://bunty-uploads --region us-east-1

Use in Application

import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const s3 = new S3Client({ region: 'us-east-1' });

async function upload(key: string, body: Buffer) {
  await s3.send(new PutObjectCommand({
    Bucket: 'bunty-uploads',
    Key: key,
    Body: body,
  }));
}

Secrets Manager

Store Secrets

aws secretsmanager create-secret \
  --name bunty/production \
  --secret-string '{"DATABASE_URL":"postgresql://...","JWT_SECRET":"..."}'

Access in Application

import { SecretsManagerClient, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';

const client = new SecretsManagerClient({ region: 'us-east-1' });

async function getSecrets() {
  const response = await client.send(
    new GetSecretValueCommand({ SecretId: 'bunty/production' })
  );
  return JSON.parse(response.SecretString);
}

CloudWatch Monitoring

Enable Logging

import { CloudWatchLogsClient, PutLogEventsCommand } from '@aws-sdk/client-cloudwatch-logs';

const logs = new CloudWatchLogsClient({ region: 'us-east-1' });

async function log(message: string) {
  await logs.send(new PutLogEventsCommand({
    logGroupName: '/aws/bunty-app',
    logStreamName: '2024/01/01',
    logEvents: [{
      timestamp: Date.now(),
      message,
    }],
  }));
}

CloudWatch Alarms

aws cloudwatch put-metric-alarm \
  --alarm-name bunty-high-cpu \
  --alarm-description "Alert when CPU exceeds 80%" \
  --metric-name CPUUtilization \
  --namespace AWS/ECS \
  --statistic Average \
  --period 300 \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold

CI/CD with CodePipeline

buildspec.yml

version: 0.2

phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $ECR_REPO
  build:
    commands:
      - echo Build started on `date`
      - docker build -t bunty-app .
      - docker tag bunty-app:latest $ECR_REPO:$IMAGE_TAG
  post_build:
    commands:
      - docker push $ECR_REPO:$IMAGE_TAG
      - printf '[{"name":"bunty-app","imageUri":"%s"}]' $ECR_REPO:$IMAGE_TAG > imagedefinitions.json

artifacts:
  files: imagedefinitions.json

Cost Optimization

  • Use Spot Instances for non-critical workloads
  • Enable auto-scaling
  • Use S3 Intelligent-Tiering
  • Set up CloudWatch billing alarms
  • Use Reserved Instances for predictable workloads

Next Steps

Have questions? Join our Discord community
Found an issue? Edit this page on GitHub