Skip to main content

Overview

GenLayer Points is deployed on AWS using:
  • AWS App Runner - Backend Django application (containerized)
  • AWS Amplify - Frontend Svelte application
  • AWS RDS - PostgreSQL database
  • AWS Systems Manager (SSM) - Environment variable management

Architecture

Prerequisites

1

Install AWS CLI

# macOS
brew install awscli

# Linux
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install

# Verify installation
aws --version
2

Configure AWS credentials

aws configure
Enter:
  • AWS Access Key ID
  • AWS Secret Access Key
  • Default region: us-east-1
  • Default output format: json
3

Install Docker

Required for building and pushing container images:

Database Setup

Create RDS PostgreSQL Instance

# Create DB subnet group
aws rds create-db-subnet-group \
    --db-subnet-group-name points-subnet-group \
    --db-subnet-group-description "Subnet group for Points database" \
    --subnet-ids subnet-xxxxxx subnet-yyyyyy

# Create security group
aws ec2 create-security-group \
    --group-name points-db-sg \
    --description "Security group for Points RDS" \
    --vpc-id vpc-xxxxxx

# Allow PostgreSQL traffic
aws ec2 authorize-security-group-ingress \
    --group-id sg-xxxxxx \
    --protocol tcp \
    --port 5432 \
    --source-group sg-xxxxxx  # App Runner security group

# Create RDS instance
aws rds create-db-instance \
    --db-instance-identifier points-db \
    --db-instance-class db.t3.micro \
    --engine postgres \
    --engine-version 15.4 \
    --master-username pointsadmin \
    --master-user-password YourSecurePassword123! \
    --allocated-storage 20 \
    --db-subnet-group-name points-subnet-group \
    --vpc-security-group-ids sg-xxxxxx \
    --backup-retention-period 7 \
    --storage-encrypted \
    --publicly-accessible false
Wait for the database to become available:
aws rds wait db-instance-available --db-instance-identifier points-db
Get the endpoint:
aws rds describe-db-instances \
    --db-instance-identifier points-db \
    --query 'DBInstances[0].Endpoint.Address' \
    --output text

Store Database URL in Parameter Store

# Format: postgresql://username:password@endpoint:5432/database
aws ssm put-parameter \
    --name "/tally/prod/database_url" \
    --value "postgresql://pointsadmin:YourSecurePassword123!@points-db.xxxxxx.us-east-1.rds.amazonaws.com:5432/points" \
    --type "SecureString"

VPC Connector Setup

A VPC connector allows App Runner to securely access RDS:
# Create VPC connector
aws apprunner create-vpc-connector \
    --vpc-connector-name points-vpc-connector \
    --subnets subnet-xxxxxx subnet-yyyyyy \
    --security-groups sg-xxxxxx

# Get the ARN (needed for deployment)
aws apprunner list-vpc-connectors \
    --query "VpcConnectors[?VpcConnectorName=='points-vpc-connector'].VpcConnectorArn" \
    --output text
Save the VPC Connector ARN - you’ll need it for the deployment script.

Environment Variables

Store all configuration in AWS Systems Manager Parameter Store:
# Django Configuration
aws ssm put-parameter --name "/tally/prod/secret_key" --value "$(python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())')" --type "SecureString"
aws ssm put-parameter --name "/tally/prod/debug" --value "False" --type "String"
aws ssm put-parameter --name "/tally/prod/allowed_hosts" --value "your-domain.com,your-app-runner-url.us-east-1.awsapprunner.com" --type "String"

# Database (already created above)
aws ssm put-parameter --name "/tally/prod/database_url" --value "postgresql://user:pass@host:5432/db" --type "SecureString"

# CORS & CSRF
aws ssm put-parameter --name "/tally/prod/csrf_trusted_origins" --value "https://your-domain.com" --type "String"
aws ssm put-parameter --name "/tally/prod/siwe_domain" --value "your-domain.com" --type "String"

# Blockchain
aws ssm put-parameter --name "/tally/prod/validator_contract_address" --value "0x10eCB157734c8152f1d84D00040c8AA46052CB27" --type "String"
aws ssm put-parameter --name "/tally/prod/validator_rpc_url" --value "https://zksync-os-testnet-genlayer.zksync.dev" --type "String"

# reCAPTCHA (get from https://www.google.com/recaptcha/admin)
aws ssm put-parameter --name "/tally/prod/recaptcha_public_key" --value "your-site-key" --type "String"
aws ssm put-parameter --name "/tally/prod/recaptcha_private_key" --value "your-secret-key" --type "SecureString"

# GitHub OAuth (optional)
aws ssm put-parameter --name "/tally/prod/github_client_id" --value "your-client-id" --type "String"
aws ssm put-parameter --name "/tally/prod/github_client_secret" --value "your-client-secret" --type "SecureString"
aws ssm put-parameter --name "/tally/prod/github_encryption_key" --value "$(python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())')" --type "SecureString"
Production parameters use /tally/prod/* prefix. Development uses /tally-backend/dev/* prefix. Make sure to use the correct prefix for your environment.

Backend Deployment

The deploy-apprunner.sh script automates the entire deployment:
1

Navigate to backend directory

cd backend
2

Make script executable

chmod +x deploy-apprunner.sh
3

Deploy with VPC connector

# Production deployment
./deploy-apprunner.sh tally-backend arn:aws:apprunner:us-east-1:ACCOUNT_ID:vpcconnector/points-vpc-connector

# Development deployment
./deploy-apprunner-dev.sh tally-backend-dev arn:aws:apprunner:us-east-1:ACCOUNT_ID:vpcconnector/points-vpc-connector
What the script does:
  1. Builds Docker image from Dockerfile
  2. Pushes to Amazon ECR
  3. Creates or updates App Runner service
  4. Configures VPC connector
  5. Loads environment variables from SSM Parameter Store
  6. Waits for deployment to complete

Manual Deployment

If you prefer manual control:
1

Build and push Docker image

# 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

# Create repository
aws ecr create-repository --repository-name tally-backend

# Build image
docker build -t tally-backend .

# Tag image
docker tag tally-backend:latest ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/tally-backend:latest

# Push image
docker push ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/tally-backend:latest
2

Create IAM roles

# Create instance role for App Runner
aws iam create-role \
    --role-name AppRunnerInstanceRole \
    --assume-role-policy-document file://trust-policy.json

# Attach SSM read policy
aws iam attach-role-policy \
    --role-name AppRunnerInstanceRole \
    --policy-arn arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
3

Create App Runner service

Create a file apprunner-config.json:
{
  "ServiceName": "tally-backend",
  "SourceConfiguration": {
    "ImageRepository": {
      "ImageIdentifier": "ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/tally-backend:latest",
      "ImageRepositoryType": "ECR",
      "ImageConfiguration": {
        "Port": "8000",
        "RuntimeEnvironmentVariables": {}
      }
    },
    "AutoDeploymentsEnabled": false
  },
  "InstanceConfiguration": {
    "Cpu": "1024",
    "Memory": "2048",
    "InstanceRoleArn": "arn:aws:iam::ACCOUNT_ID:role/AppRunnerInstanceRole"
  },
  "HealthCheckConfiguration": {
    "Protocol": "HTTP",
    "Path": "/",
    "Interval": 10,
    "Timeout": 5,
    "HealthyThreshold": 1,
    "UnhealthyThreshold": 5
  },
  "NetworkConfiguration": {
    "EgressConfiguration": {
      "EgressType": "VPC",
      "VpcConnectorArn": "arn:aws:apprunner:us-east-1:ACCOUNT_ID:vpcconnector/points-vpc-connector"
    }
  }
}
Deploy:
aws apprunner create-service --cli-input-json file://apprunner-config.json

Run Migrations

After first deployment, run Django migrations:
# SSH into App Runner (via AWS Console logs) or use startup script
python manage.py migrate
python manage.py createsuperuser
Or include in Dockerfile:
# Add to startup
CMD python manage.py migrate && \
    gunicorn backend.wsgi:application --bind 0.0.0.0:8000

Frontend Deployment

Using AWS Amplify

1

Connect to GitHub

  1. Go to AWS Amplify in AWS Console
  2. Click New appHost web app
  3. Select GitHub and authorize
  4. Choose your repository and branch (main or dev)
2

Configure build settings

Amplify will auto-detect amplify.yml. Ensure it contains:
version: 1
applications:
  - appRoot: frontend
    frontend:
      phases:
        preBuild:
          commands:
            - npm ci
        build:
          commands:
            - npm run build
      artifacts:
        baseDirectory: dist
        files:
          - '**/*'
      cache:
        paths:
          - node_modules/**/*
3

Set environment variables

In Amplify console, go to Environment variables and add:
VITE_API_URL=https://your-app-runner-url.us-east-1.awsapprunner.com
VITE_VALIDATOR_RPC_URL=https://zksync-os-testnet-genlayer.zksync.dev
VITE_VALIDATOR_CONTRACT_ADDRESS=0x10eCB157734c8152f1d84D00040c8AA46052CB27
VITE_EXPLORER_URL=https://explorer-asimov.genlayer.com
VITE_RECAPTCHA_SITE_KEY=your-site-key
VITE_GOOGLE_ANALYTICS_ID=G-XXXXXXXXXX
4

Deploy

Click Save and deploy. Amplify will:
  1. Clone your repository
  2. Install dependencies
  3. Build the Svelte app
  4. Deploy to CloudFront CDN
Your app will be available at: https://main.xxxxxx.amplifyapp.com
5

Configure custom domain (optional)

  1. Go to Domain management
  2. Click Add domain
  3. Enter your domain (e.g., points.genlayer.com)
  4. Follow DNS configuration instructions
  5. Wait for SSL certificate provisioning

Post-Deployment

Update CORS and CSRF

Once you have your frontend URL, update backend settings:
# Update CORS allowed origins
aws ssm put-parameter \
    --name "/tally/prod/allowed_hosts" \
    --value "points.genlayer.com,your-app-runner-url.us-east-1.awsapprunner.com" \
    --overwrite

# Update CSRF trusted origins
aws ssm put-parameter \
    --name "/tally/prod/csrf_trusted_origins" \
    --value "https://points.genlayer.com" \
    --overwrite

# Update SIWE domain
aws ssm put-parameter \
    --name "/tally/prod/siwe_domain" \
    --value "points.genlayer.com" \
    --overwrite
Then redeploy the backend:
cd backend
./deploy-apprunner.sh tally-backend

Update Frontend API URL

In Amplify environment variables:
VITE_API_URL=https://your-app-runner-url.us-east-1.awsapprunner.com
Trigger a new build in Amplify console.

CI/CD Setup

Backend CI/CD (GitHub Actions)

Create .github/workflows/deploy-backend.yml:
name: Deploy Backend to App Runner

on:
  push:
    branches: [ main ]
    paths:
      - 'backend/**'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1
      
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v1
      
      - name: Build and push Docker image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: tally-backend
          IMAGE_TAG: ${{ github.sha }}
        run: |
          cd backend
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
      
      - name: Deploy to App Runner
        run: |
          aws apprunner update-service \
            --service-arn ${{ secrets.APP_RUNNER_SERVICE_ARN }} \
            --source-configuration ImageRepository={ImageIdentifier=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG}

Frontend CI/CD

Amplify automatically deploys on push to connected branch. No additional configuration needed!

Monitoring

App Runner Logs

View logs in AWS Console:
  1. Go to App Runner → Your service
  2. Click Logs tab
  3. View Application logs and System logs
Or use CLI:
aws apprunner describe-service --service-arn YOUR_ARN

Amplify Logs

  1. Go to Amplify → Your app
  2. Click on a deployment
  3. View build logs

CloudWatch

Both App Runner and Amplify send logs to CloudWatch:
aws logs tail /aws/apprunner/tally-backend --follow

Scaling

App Runner Auto Scaling

aws apprunner update-service \
    --service-arn YOUR_ARN \
    --auto-scaling-configuration-arn arn:aws:apprunner:us-east-1:ACCOUNT_ID:autoscalingconfiguration/HighConcurrency
Or create custom configuration:
aws apprunner create-auto-scaling-configuration \
    --auto-scaling-configuration-name HighConcurrency \
    --max-concurrency 100 \
    --min-size 1 \
    --max-size 10

RDS Scaling

Upgrade instance class:
aws rds modify-db-instance \
    --db-instance-identifier points-db \
    --db-instance-class db.t3.small \
    --apply-immediately

Costs

Estimated Monthly Costs (US East 1):
  • App Runner: $25-50/month (1 vCPU, 2GB RAM)
  • RDS db.t3.micro: $15-20/month (free tier eligible for 12 months)
  • Amplify: $0-5/month (generous free tier)
  • Data Transfer: $5-10/month
  • Total: ~$45-85/month
AWS Free Tier covers RDS for 12 months and includes 750 hours/month of App Runner build time.

Troubleshooting

App Runner Health Checks Failing

Solution: Ensure Django returns 200 OK at root path:
# backend/api/views.py
def health_check(request):
    return JsonResponse({'status': 'healthy'})

# backend/urls.py
urlpatterns = [
    path('', health_check),
    # ...
]

Database Connection Refused

Solutions:
  • Verify VPC connector is attached
  • Check RDS security group allows traffic from App Runner subnets
  • Verify database URL in Parameter Store is correct

CORS Errors

Solutions:
  • Add Amplify URL to ALLOWED_HOSTS
  • Add https:// prefix to CSRF_TRUSTED_ORIGINS
  • Redeploy backend after changes

Next Steps

Environment Variables

Complete list of configuration options

Backend Setup

Local backend development

Frontend Setup

Local frontend development

Architecture

System architecture overview

Build docs developers (and LLMs) love