Skip to main content
This guide provides a comprehensive, step-by-step walkthrough for self-hosting Flexprice on AWS in a production-ready setup. It covers VPC networking, ECS compute (EC2 with ARM64), Aurora PostgreSQL, Amazon MSK (Kafka), EKS with ClickHouse, ElastiCache Redis, DynamoDB, IAM, secrets management, and observability.

Prerequisites

Before you begin, ensure you have the following:
An AWS account with administrator or equivalent permissions to create VPCs, ECS, RDS, MSK, EKS, S3, IAM roles, and CloudWatch resources
AWS CLI v2 installed and configured with credentials (aws configure)
Docker installed (for building and pushing images to ECR)
kubectl installed (for EKS/ClickHouse management)
eksctl installed (optional but recommended for EKS cluster creation)
Helm installed (for ClickHouse deployment)

Region selection

Choose an AWS region that:
  • Has all required services (see Cost estimation for the list)
  • Is geographically close to your users for lower latency
  • Meets your compliance requirements (e.g., GDPR for EU data)
This guide uses us-east-1 as the example region. Replace with your preferred region.

Cost estimation

We provide two configurations: a development setup for testing and a production setup for high-throughput workloads (100M+ events/month).
Costs vary by region and usage. Use the AWS Pricing Calculator for accurate estimates. ARM64/Graviton instances provide ~20% cost savings over x86.

Sizing for 100M events/month

Traffic and storage estimates:
  • 100M events/month = ~38.5 events/second average
  • Peak traffic: 150-200 events/second (4-5x burst)
  • ClickHouse storage: ~50 GB/month growth
  • DynamoDB: ~20 GB/month growth

Architecture overview

Flexprice on AWS runs with the following production architecture: AWS architecture for Flexprice Data flow:
  • ClientsCloudflare (DNS, WAF, rate limiting) → ALBECS (API, Consumer, Temporal Worker)
  • API writes to Aurora PostgreSQL, publishes events to MSK (Kafka) and DynamoDB
  • Consumer reads from Kafka and writes to ClickHouse (on EKS) for analytics
  • Temporal Worker connects to Temporal Cloud for workflow orchestration
  • ElastiCache Redis provides caching in cluster mode
  • S3 stores invoice PDFs; CloudWatch and Grafana Cloud collect logs and metrics
This guide uses Temporal Cloud (recommended for production). You can also self-host Temporal, but it requires additional infrastructure. Cloudflare is optional but recommended for DNS and WAF.

Component summary


Step 1: VPC and networking

Create a VPC with public and private subnets across two Availability Zones for high availability. Unless otherwise specified, create each resource in this guide via AWS Console, CLI, or IaC using the configuration described in the tables.

VPC configuration

Create VPC with AWS CLI

Create the VPC, enable DNS hostnames, and attach an Internet Gateway.

Create subnets

Create public and private subnets in two Availability Zones using the CIDRs in the VPC configuration table.

Create NAT Gateway

Create an Elastic IP and NAT Gateway in a public subnet.

Create route tables

Create public and private route tables and associate subnets (public: default route to Internet Gateway; private: default route to NAT Gateway).

Create security groups

Create security groups for ALB, ECS, RDS, MSK, and EKS. Use the rules in the summary table below.

Security group rules summary

For production, consider restricting the ALB security group to only Cloudflare IP ranges if you’re using Cloudflare for DNS and WAF.

Step 2: IAM roles and policies

Create IAM roles for ECS task execution and task runtime permissions.

ECS Task Execution Role

This role allows ECS to pull container images and write logs. Create the role and attach the managed policy AmazonECSTaskExecutionRolePolicy plus an inline policy for Secrets Manager access.

ECS Task Role

This role grants permissions for the Flexprice application at runtime (S3, CloudWatch Logs, Secrets Manager). Create the task role and attach the inline policy.

Step 3: Secrets Manager

Store sensitive configuration in AWS Secrets Manager.

Create secrets

Create secrets for PostgreSQL, ClickHouse, Kafka (SASL), auth, and Temporal Cloud. Store postgres (host, username, password, database), clickhouse (username, password), kafka (username, password), auth (64-char hex secret), and temporal (API key, key name, namespace) as needed.
Replace placeholder values with strong, unique credentials. Use a password generator for production secrets.

Step 4: Aurora PostgreSQL

Create an Aurora PostgreSQL cluster for Flexprice’s primary database. Aurora provides higher availability and performance compared to standard RDS.
Create a DB subnet group, Aurora cluster (with Secrets Manager managed credentials), writer instance (db.r8g.xlarge), and reader instance in the other AZ. Retrieve the cluster writer and reader endpoints for application configuration.

Aurora configuration summary

Update Secrets Manager with Aurora endpoints

Update the postgres secret in Secrets Manager with the Aurora writer and reader endpoints and the managed master password ARN.
Aurora with Secrets Manager managed credentials automatically rotates the master password. Use the MasterUserSecret ARN to retrieve the current password.

Run database migrations

You can run migrations using a one-off ECS task or from a bastion host. Create a migration task definition and run it via ECS (or run flexprice migrate up from a host with DB access) using the configuration described above.

Step 5: Amazon MSK (Kafka)

Create an Amazon MSK cluster for event streaming.

Create MSK configuration

Create an MSK configuration (server properties) and register it.

Create MSK cluster

Create the MSK cluster with 2 brokers (1 per AZ), kafka.m5.large (4 vCPU, 8 GB) instance type, and 1024 GB (1 TB) storage per broker. Enable SASL/SCRAM, TLS, encryption at rest, and enhanced monitoring.

Create SASL/SCRAM secret for MSK

Create a secret in Secrets Manager with the prefix AmazonMSK_ and associate it with the MSK cluster.

Get MSK bootstrap brokers

Retrieve the SASL/SCRAM bootstrap broker string from the MSK cluster (AWS Console or CLI) for application configuration.

Create Kafka topics

Use a bastion host or an EC2 instance with Kafka CLI tools to create the events and events-dlq topics (e.g. 6 partitions, replication factor 2). Use SASL_SSL and SCRAM-SHA-512 in client configuration.

MSK configuration summary

For development, use kafka.t3.small with 100 GB storage. For production (100M+ events/month), use 2 brokers, kafka.m5.large, and 1 TB storage per broker.

Step 6: EKS with ClickHouse

Create an EKS cluster and deploy ClickHouse for analytics storage. For production (100M+ events/month), use m5.8xlarge nodes for the ClickHouse node group.

Create EKS cluster with eksctl

Create an EKS cluster with a managed node group (m5.8xlarge for production) via eksctl or IaC. Use private subnets and attach the EKS security group.

Create gp3 StorageClass

Create a gp3 StorageClass (EBS CSI driver, encrypted, Retain, WaitForFirstConsumer) via kubectl or IaC.

Create ClickHouse namespace and secrets

Create the clickhouse namespace and a Kubernetes secret with credentials from Secrets Manager via kubectl or IaC.

Deploy ClickHouse with Helm

Add the Altinity ClickHouse Helm repo and install the ClickHouse Operator in the clickhouse namespace via Helm.

Create ClickHouse cluster

Deploy a ClickHouseInstallation (Altinity operator) with the credentials secret, gp3 storage, and appropriate resources via kubectl or Helm.

Create ClickHouse service for ECS access

Create a ClusterIP Service for ClickHouse (ports 9000, 8123) targeting the ClickHouse installation via kubectl or IaC.

Get ClickHouse endpoint

For ECS tasks to access ClickHouse, you have several options:
  1. Internal NLB (recommended): Create an internal Network Load Balancer pointing to the ClickHouse service
  2. VPC peering/Transit Gateway: If ECS and EKS are in separate VPCs
  3. AWS PrivateLink: For cross-account access
Create the internal NLB (type LoadBalancer with internal annotation) and use its DNS name as the ClickHouse endpoint (port 9000) for ECS configuration.

Initialize ClickHouse database

Connect to ClickHouse (e.g. via port-forward or the NLB) and create the flexprice database using clickhouse-client.

Step 7: ElastiCache Redis

Create an ElastiCache Redis cluster for caching and session management.

Create Redis subnet group

Create a cache subnet group in the data subnets.

Create Redis security group

Create a security group for Redis allowing TCP 6379 from the ECS security group.

Create Redis replication group (cluster mode)

Create a Redis replication group with cache.r6g.large, cluster mode, multi-node (e.g. multiple node groups for ~$600/month), TLS and at-rest encryption, and multi-AZ.

Redis configuration summary


Step 8: DynamoDB

Create a DynamoDB table for durable event storage alongside ClickHouse.

Create events table

Create a DynamoDB table named events with partition key pk (String) and sort key sk (String), on-demand billing.

Enable Point-in-Time Recovery

Enable point-in-time recovery (continuous backups) on the events table.

DynamoDB configuration summary

DynamoDB is used alongside ClickHouse for durable event storage. Events are written to both DynamoDB (for durability) and ClickHouse (for analytics).

Step 9: S3 and CloudWatch

Create S3 bucket for invoices

Create an S3 bucket for invoice PDFs with versioning, AES256 encryption, block public access, and optional lifecycle rules (e.g. transition to STANDARD_IA after 90 days).

Create CloudWatch log groups

Create log groups for ECS services (api, worker, temporal-worker, migration) with a retention policy (e.g. 30 days).

Create CloudWatch alarms

Create alarms for ECS API CPU, RDS CPU, and RDS connections (e.g. threshold 80%, 2 evaluation periods) and associate with an SNS topic for alerts.

Step 10: ECR and container images

Create ECR repositories

Create ECR repositories for api, worker, and temporal-worker with scan-on-push and AES256 encryption.

Build and push images

Build Flexprice container images (api, worker, temporal-worker), authenticate to ECR, tag and push to your ECR repositories.

Step 11: ECS cluster and services

Create ECS cluster

For production (100M+ events/month), create an ECS cluster with EC2 capacity: launch template with m6g.xlarge (ARM64/Graviton), Auto Scaling Group with 10 nodes (min/max as needed), capacity provider with managed scaling, and associate with the cluster.

Create API task definition

Register an ECS task definition for the API service: production uses EC2/ARM64 (768 CPU, 1536 memory) with bridge network; development uses Fargate (1024 CPU, 2048 memory). Include environment variables and secrets from Secrets Manager (auth, postgres, clickhouse, kafka, temporal). Set FLEXPRICE_DEPLOYMENT_MODE=api, health check on :8080/health, and CloudWatch log group. See Step 12 for the full environment variable reference.

Create Worker task definition

Register an ECS task definition for the Consumer (worker) service: FLEXPRICE_DEPLOYMENT_MODE=consumer, postgres/clickhouse/kafka secrets from Secrets Manager. For production use 30 tasks (100M events/month). See Step 12 for environment variables.

Create Temporal Worker task definition

Register an ECS task definition for the Temporal Worker: FLEXPRICE_DEPLOYMENT_MODE=temporal_worker, postgres/clickhouse/kafka/temporal secrets. See Step 12 for environment variables.

Create Application Load Balancer

Create an internet-facing Application Load Balancer in the public subnets, a target group (HTTP 8080, health check /health), an HTTPS listener with an ACM certificate, and an HTTP listener that redirects to HTTPS.

Create ECS services

Create ECS services for API (desired count 6 for production), Worker/Consumer (desired count 30 for production), and Temporal Worker (e.g. 3 tasks). Attach the API service to the ALB target group. Use private subnets and the ECS security group.

Configure Auto Scaling

Register scalable targets and target-tracking scaling policies for the API (and optionally Worker) services (e.g. min/max desired count, CPU target 70%).

Step 12: Environment variables reference

Below is a complete reference of environment variables for each service. Variables marked with (secret) should be stored in AWS Secrets Manager.

API service

Worker and Temporal Worker services

Worker and Temporal Worker use the same variables as API, with FLEXPRICE_DEPLOYMENT_MODE set to consumer or temporal_worker respectively; omit FLEXPRICE_SERVER_ADDRESS for both.

Additional environment variables (Production)

These variables are used in production deployments:

Step 13: Temporal Cloud configuration

Temporal Cloud is the recommended workflow orchestration service for production deployments.

Sign up for Temporal Cloud

  1. Go to temporal.io/cloud
  2. Create an account and organization
  3. Create a namespace (e.g., flexprice-prod-usa)

Create service account and API key

  1. In Temporal Cloud console, go to Settings > API Keys
  2. Create a new API key with appropriate permissions
  3. Note the API key and key name

Store Temporal credentials

Temporal environment variables

Temporal Cloud provides managed infrastructure, automatic upgrades, and 99.99% SLA. For self-hosted Temporal, refer to the Temporal documentation.

Step 14: Third-party integrations (Optional)

Configure optional third-party services for enhanced functionality.

Supabase (Authentication)

If using Supabase for authentication:

Svix (Webhooks)

For webhook delivery via Svix:

Sentry (Error Tracking)

For error tracking with Sentry:

Grafana Cloud (Observability)

For profiling with Pyroscope on Grafana Cloud:

FluentD (Log Aggregation)

For centralized logging with FluentD:

Resend (Email)

For transactional emails via Resend:

Third-party cost summary

Breakdown below; production total is in the Cost estimation table above.

Deployment checklist

Use this checklist to verify your deployment:
1

VPC and Networking

  • VPC created with correct CIDR
  • 2 public subnets created
  • 4 private subnets created (2 compute, 2 data)
  • Internet Gateway attached
  • NAT Gateway(s) created and running
  • Route tables configured correctly
  • Security groups created with correct rules
2

IAM

  • ECS Task Execution Role created
  • ECS Task Role created
  • Policies attached correctly (S3, Secrets Manager, CloudWatch, DynamoDB)
3

Secrets Manager

  • PostgreSQL/Aurora credentials stored
  • ClickHouse credentials stored
  • Kafka SASL credentials stored
  • Auth secret stored
  • Temporal Cloud credentials stored
  • Third-party credentials stored (Supabase, Svix, etc.)
4

Aurora PostgreSQL

  • DB subnet group created
  • Aurora cluster created and available
  • Writer and Reader instances running
  • Security group allows ECS access
  • Secrets Manager updated with endpoints
  • Database migrations completed
5

Amazon MSK

  • MSK cluster created and active
  • SASL/SCRAM secret associated
  • Topics created (events, events_lazy, events-dlq)
  • Security group allows ECS access
  • Prometheus exporters enabled
6

EKS and ClickHouse

  • EKS cluster created
  • Node group running
  • gp3 StorageClass created
  • ClickHouse operator installed
  • ClickHouse cluster deployed
  • NLB created for ClickHouse access
  • Database initialized
7

ElastiCache Redis

  • Redis subnet group created
  • Redis replication group created
  • Cluster mode enabled (production)
  • TLS encryption enabled
  • Security group allows ECS access
8

DynamoDB

  • Events table created
  • Point-in-time recovery enabled
  • IAM policy allows ECS access
9

S3 and CloudWatch

  • S3 bucket created with encryption
  • CloudWatch log groups created
  • CloudWatch alarms configured
10

ECR and Images

  • ECR repositories created
  • Container images built and pushed
11

ECS

  • ECS cluster created
  • Task definitions registered
  • ALB created with HTTPS listener
  • Target group configured
  • Services created and healthy
  • Auto Scaling configured
12

Verification

  • API health check passing
  • Worker consuming from Kafka
  • Temporal workflows executing
  • Logs appearing in CloudWatch

Troubleshooting

API unreachable

  1. Check ALB health checks:
  2. Check ECS task status:
  3. Check ECS task logs:
  4. Verify security groups:
    • ALB SG allows inbound 443 from internet
    • ECS SG allows inbound 8080 from ALB SG
    • ECS SG allows outbound to RDS, MSK, ClickHouse

Worker not consuming

  1. Check Kafka connectivity:
  2. Check consumer lag in MSK CloudWatch metrics
  3. Verify SASL credentials:
    • Ensure AmazonMSK_ prefixed secret is associated with cluster
    • Verify username/password match in Secrets Manager
  4. Check security group:
    • MSK SG allows inbound 9094/9096 from ECS SG

Temporal workflows failing

  1. Check Temporal Worker logs:
  2. Verify Temporal Cloud connection:
    • Correct FLEXPRICE_TEMPORAL_ADDRESS
    • Valid API key and namespace
    • TLS enabled
  3. Check Temporal Cloud UI for workflow history and errors

ClickHouse connection errors

  1. Verify ClickHouse pods are running:
  2. Check ClickHouse logs:
  3. Verify NLB is healthy:
  4. Test connectivity from ECS:
    • Ensure EKS SG allows inbound 9000 from ECS SG
    • Verify NLB DNS resolves correctly

RDS connection issues

  1. Verify RDS is available:
  2. Check security group:
    • RDS SG allows inbound 5432 from ECS SG
  3. Verify credentials:
    • Check Secrets Manager values match RDS configuration
  4. Test from bastion:

Scaling guidelines

Scale when metrics exceed the thresholds below.

Cost optimization

Reserved instances

  • RDS: Purchase Reserved Instances for 1-3 year commitment (up to 72% savings)
  • MSK: Not available; consider Kafka on EC2 with Reserved Instances for significant savings

Fargate Spot

Use Fargate Spot for non-critical workloads:

S3 lifecycle policies

Already configured to transition to IA after 90 days. Consider:
  • Glacier for archives > 1 year
  • Intelligent-Tiering for unpredictable access patterns

CloudWatch log retention

Set appropriate retention periods:
  • Production: 30-90 days
  • Development: 7-14 days
  • Archive to S3 for long-term storage

Additional resources

Configuration Reference

Complete list of Flexprice environment variables

Architecture Overview

Understand Flexprice’s internal architecture

Monitoring

Set up monitoring and observability

Troubleshooting

Common issues and solutions

Need help?

If you encounter issues during deployment: