Kaapi uses a modern cloud-native deployment architecture built on AWS services with automated CI/CD pipelines.
The deployment follows a containerized approach where:
- Application code is packaged into Docker images
- Images are stored in AWS ECR (Elastic Container Registry)
- ECS (Elastic Container Service) runs and manages the containers
- GitHub Actions automates the build and deployment process
The deployment pipeline is triggered automatically:
- Code Push: Developer pushes code to GitHub
- Build: GitHub Actions builds Docker image
- Push: Image is pushed to ECR
- Deploy: ECS pulls new image and updates running tasks
Two deployment environments are configured:
- Staging: Deployed on every push to
mainbranch for testing - Production: Deployed only on version tags for stable releases
Before deploying, ensure the following AWS infrastructure exists:
- ECS Clusters: Separate clusters for staging and production environments
- ECR Repositories: Container image repositories for each environment
- ECS Task Definitions: Define container configurations, resource limits, and environment variables
- ECS Services: Manage the desired number of running tasks
- IAM Role for GitHub: Allows GitHub Actions to authenticate via OIDC (no long-lived credentials)
- RDS PostgreSQL: Managed database service (recommended for production)
- ElastiCache Redis: Managed Redis for caching (optional)
- Amazon MQ RabbitMQ: Managed message broker for Celery (optional)
Configure GitHub to allow automated deployments:
- Environment: Create
AWS_ENV_VARSenvironment for deployment protection - Variable: Set
AWS_RESOURCE_PREFIXto identify your AWS resources
All AWS resources follow a consistent naming pattern using AWS_RESOURCE_PREFIX as the base identifier.
This naming convention ensures clear separation between environments and easy identification of resources.
Purpose: Automatically deploy changes to a testing environment for validation before production.
Trigger: Push to main branch
git push origin mainWorkflow Steps (.github/workflows/cd-staging.yml):
- Checkout: Clone the repository code
- AWS Authentication: Use OIDC to authenticate (no stored credentials)
- ECR Login: Authenticate to container registry
- Build: Create Docker image from
./backenddirectory - Push: Upload image to staging ECR repository with
latesttag - Deploy: Force ECS to pull and deploy the new image
The deployment typically completes in 5-10 minutes depending on image size and ECS configuration.
Purpose: Deploy stable, tested versions to the production environment.
Trigger: Create and push a version tag
# Create a version tag
git tag v1.0.0
# Push the tag to trigger deployment
git push origin v1.0.0Workflow Steps (.github/workflows/cd-production.yml):
- Checkout: Clone the repository at the tagged version
- AWS Authentication: Use OIDC to authenticate
- ECR Login: Authenticate to container registry
- Build: Create Docker image from
./backenddirectory - Push: Upload image to production ECR repository with
latesttag - Deploy: Force ECS to pull and deploy the new image
Best Practice: Use semantic versioning (e.g., v1.0.0, v1.2.3) to clearly identify releases.
Environments in GitHub provide deployment protection and organization.
- Go to repository Settings → Environments
- Click New environment
- Name it:
AWS_ENV_VARS - Optionally, add protection rules (e.g., required reviewers)
Variables store non-sensitive configuration that workflows need.
- Go to Settings → Secrets and variables → Actions → Variables tab
- Click New repository variable
- Add:
- Name:
AWS_RESOURCE_PREFIX - Value: Your AWS resource prefix (e.g.,
kaapi)
- Name:
The workflows use AWS OIDC authentication, which is more secure than storing AWS access keys:
- No long-lived credentials stored in GitHub
- AWS IAM role assumes identity based on GitHub's OIDC token
- Permissions are scoped to specific actions
The IAM role ARN is configured in workflow files:
role-to-assume: arn:aws:iam::{YOUR_AWS_ACCOUNT_ID}:role/github-action-role
aws-region: {YOUR_AWS_REGION}Note: Replace {YOUR_AWS_ACCOUNT_ID} with your AWS account ID and {YOUR_AWS_REGION} with your chosen region (e.g., ap-south-1, us-east-1).
Application configuration is managed through environment variables set in ECS Task Definitions. These are injected into containers at runtime.
Environment variables in ECS Task Definitions include database credentials, AWS credentials, API keys, and service endpoints. Refer to .env.example in the repository for a complete list of required and optional variables.
Key categories:
- Authentication & Security: JWT keys, admin credentials
- Database: PostgreSQL connection details
- AWS Services: S3 access credentials
- Background Tasks: RabbitMQ and Redis endpoints
- Optional: OpenAI API key, Sentry DSN
Use Python to generate cryptographically secure keys:
python -c "import secrets; print(secrets.token_urlsafe(32))"Run this multiple times to generate different keys for SECRET_KEY, passwords, etc.
Database schema changes must be applied before deploying new application versions. This ensures the database structure matches what the code expects.
Run migrations as a one-time ECS task:
aws ecs run-task \
--cluster {prefix}-cluster \
--task-definition {migration-task-def} \
--region {YOUR_AWS_REGION}This runs the migration in the same environment as your application, ensuring consistency.
For testing migrations locally:
cd backend
uv run alembic upgrade headImportant: Always test migrations in staging before applying to production.
Logs: View application logs from ECS tasks
aws logs tail /ecs/{cluster}/{service} --followMetrics: Monitor CPU, memory, request count, error rates
- View running tasks and their health status
- Check deployment status and history
- Monitor service events and errors
ECS performs health checks on the /api/v1/utils/health/ endpoint. If this fails, tasks are replaced automatically.
If a deployment introduces issues, rollback to a previous stable version.
aws ecs list-task-definitions --family-prefix {prefix}aws ecs update-service \
--cluster {prefix}-cluster \
--service {prefix}-service \
--task-definition {previous-task-def-arn} \
--region {YOUR_AWS_REGION}ECS will perform a rolling update back to the specified task definition.
Tip: Keep track of stable task definition ARNs for quick rollbacks.