Skip to main content

Deployment Architecture

This page describes the AWS infrastructure used for the Infracast SaaS platform and the recommended architecture for self-hosted Terraform deployments.

AWS Architecture Overview

graph TB
Internet((Internet))

subgraph AWS
CF[CloudFront<br/>UI CDN]
S3UI[S3 Bucket<br/>React SPA]
ACM[ACM Certificate<br/>*.infracast.io]

subgraph VPC["VPC (10.0.0.0/16)"]
subgraph Public["Public Subnets (us-east-1a/b)"]
ALB[Application Load Balancer<br/>HTTPS :443]
NAT1[NAT Gateway<br/>AZ-a]
NAT2[NAT Gateway<br/>AZ-b]
end

subgraph Private["Private Subnets (us-east-1a/b)"]
ECS1[ECS Fargate Task<br/>vulcan-api]
ECS2[ECS Fargate Task<br/>vulcan-api replica]
end

subgraph Data["Data Subnets (us-east-1a/b)"]
RDS_P[(RDS Primary<br/>PostgreSQL 15)]
RDS_S[(RDS Standby<br/>Multi-AZ)]
end
end

S3Art[S3 Bucket<br/>Evidence Artifacts]
CW[CloudWatch<br/>Logs & Metrics]
SM[Secrets Manager<br/>DB credentials, JWT secret]
end

Internet -->|HTTPS| CF
CF --> S3UI
Internet -->|HTTPS :443| ALB
ACM --> ALB
ALB --> ECS1
ALB --> ECS2
ECS1 --> NAT1
ECS2 --> NAT2
ECS1 --> RDS_P
ECS2 --> RDS_P
RDS_P -.->|sync| RDS_S
ECS1 --> S3Art
ECS1 --> SM
ECS1 --> CW

Network Architecture

The Terraform module creates a three-tier VPC:

TierSubnetsResourcesInternet Access
Public2x (one per AZ)ALB, NAT Gateways, EIPsDirect via IGW
Private2x (one per AZ)ECS Fargate tasksOutbound via NAT
Data2x (one per AZ)RDS PostgreSQLNone

Security Groups

ResourceInboundOutbound
ALB443 from 0.0.0.0/08080 to ECS SG
ECS tasks8080 from ALB SG5432 to RDS SG, 443 to 0.0.0.0/0 (NAT)
RDS5432 from ECS SGNone

Application Load Balancer

The ALB terminates TLS and routes all traffic:

  • HTTPS listener (443) — routes to ECS target group
  • HTTP listener (80) — redirects to HTTPS
  • Health checkGET /healthz on port 8080, expects 200
  • Stickiness — disabled (stateless API)
  • Connection draining — 30 seconds (graceful shutdown)

ECS Fargate

The Infracast API runs as ECS Fargate tasks:

ParameterDevelopmentProduction
CPU512 (0.5 vCPU)1024 (1 vCPU)
Memory1024 MB2048 MB
Desired count12
Min count12
Max count310
Auto-scale target80% CPU70% CPU

Task Definition

Key environment variables injected into ECS tasks:

{
"environment": [
{"name": "DATABASE_URL", "valueFrom": "arn:aws:secretsmanager:...:db-url"},
{"name": "JWT_SECRET", "valueFrom": "arn:aws:secretsmanager:...:jwt-secret"},
{"name": "API_PORT", "value": "8080"},
{"name": "PLUGIN_DIR", "value": "/usr/local/lib/infracast/plugins"},
{"name": "CONTENT_CDN_URL", "value": "https://<your-cdn>.cloudfront.net"}
]
}

All secrets are sourced from AWS Secrets Manager — never hardcoded in task definitions.

Rolling Deployments

ECS performs zero-downtime rolling deployments:

  1. New task started with updated image
  2. Health check passes (/healthz returns 200)
  3. Task registered with ALB target group
  4. Old task deregistered and drained (30s)
  5. Old task stopped

RDS PostgreSQL

ParameterDevelopmentProduction
EnginePostgreSQL 15PostgreSQL 15
Instance classdb.t3.mediumdb.r5.large
Multi-AZNoYes
Storage20 GB gp3100 GB gp3, autoscale
EncryptionAES-256 (KMS)AES-256 (CMK)
Backup retention7 days35 days
Automated backupsYesYes
Performance InsightsNoYes

Database Migrations

Infracast runs database migrations automatically on startup using an embedded migration runner. No manual intervention is needed for upgrades.

warning

In production, ensure only one ECS task runs migrations at startup by using the leader election lock or setting desired_count = 1 during initial deployment, then scaling up after the first task is healthy.

CloudFront (UI Distribution)

The React SPA is served from CloudFront backed by S3:

  • Origin: S3 bucket with OAC (Origin Access Control)
  • Caching: Static assets cached at edge (1 year TTL with content hashing)
  • HTTPS: Enforced, HTTP redirected to HTTPS
  • Custom domain: app.infracast.io (SaaS) or your domain
Cache Busting

Static assets use content-hash filenames (e.g., main.abc123.js). The index.html has a 0-second TTL and is always fetched fresh. If the UI appears stale after an upgrade, hard-refresh (Ctrl+Shift+R / Cmd+Shift+R) or invalidate the CloudFront distribution.

DNS and SSL/TLS

SaaS

RecordTypeValue
app.infracast.ioCNAMECloudFront distribution
api.infracast.ioCNAMEALB DNS name

Self-Hosted

  1. Create an ACM certificate for your domain (e.g., infracast.example.com)
  2. Pass the ARN to the Terraform module: certificate_arn = "arn:aws:acm:..."
  3. Create a CNAME from your domain to the ALB DNS name (output by Terraform)
  4. DNS propagation typically takes 5–60 minutes

Dev vs Prod Environments

Infracast maintains separate AWS accounts for dev and prod:

AccountIDPurpose
vulcan-dev(internal)Development, staging, testing
vulcan-prod(internal)Production SaaS

Key differences:

SettingDevProd
RDS Multi-AZNoYes
NAT Gateways12
ECS task count12
Backup retention7 days35 days
Deletion protectionNoYes

Terraform Module Reference

module "vulcan" {
source = "github.com/azgardtek/vulcan//terraform/modules/vulcan-ecs"

environment = "prod"
vpc_cidr = "10.0.0.0/16"

# ECS
ecs_cpu = 1024
ecs_memory = 2048
ecs_desired_count = 2

# RDS
db_instance_class = "db.r5.large"
db_multi_az = true
db_storage_gb = 100

# Domain
domain_name = "infracast.example.com"
certificate_arn = "arn:aws:acm:us-east-1:123456789:certificate/abc"

tags = {
Project = "Infracast"
Environment = "prod"
}
}

See Deployment Guide for the full deployment walkthrough.