Skip to content

About

An interactive Mandelbrot explorer built as a cloud/platform engineering project. The backend renders fractals on demand with Go Lambda functions, the frontend is a React/Vite app delivered through CloudFront and private S3, and the infrastructure is managed with Terraform.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

40 Commits

Folders and files

Repository files navigation

Serverless Mandelbrot Renderer on AWS

An interactive Mandelbrot explorer built as a cloud/platform demo project. The backend renders fractals on demand with Go Lambda functions, the frontend is a React/Vite app delivered through CloudFront and private S3, and the infrastructure is managed with Terraform.

Live demo: mandelbrot.i-dmytro.org

Deployment-specific values such as domain names, AWS regions, AWS account IDs, bucket names, and GitHub repository owners are configured through .env, terraform.tfvars, Terraform outputs, and GitHub Actions variables.

Architecture

graph TD
    Browser["Browser\ncustom domain"]

    subgraph CDN["CDN"]
        CF["CloudFront\nACM viewer certificate"]
    end

    subgraph Frontend["Frontend"]
        S3["Private S3 bucket\nReact/Vite static assets"]
    end

    subgraph API["API"]
        APIGW["API Gateway HTTP API\nGET /render\nGET /render-distributed"]
    end

    subgraph Compute["Lambda · arm64"]
        Renderer["Renderer Lambda\nSingle-image render\nPNG response"]
        Orchestrator["Orchestrator Lambda\nFan-out coordinator\nPNG response"]
        Worker["Worker Lambda\nTile renderer\nRGBA bytes"]
    end

    subgraph Observability["Observability"]
        CW["CloudWatch Logs\nStructured JSON + EMF metrics"]
    end

    subgraph State["Terraform State"]
        TFS3["S3 bucket\nRemote state + S3 lockfile"]
    end

    subgraph CICD["CI/CD"]
        GHA["GitHub Actions OIDC deploy role\nCI: test · lint · tf validate\nCD: deploy frontend + backend"]
    end

    Browser -->|HTTPS| CF
    CF -->|OAC| S3
    Browser -->|HTTPS API calls| APIGW
    APIGW -->|GET /render| Renderer
    APIGW -->|GET /render-distributed| Orchestrator
    Orchestrator -->|lambda:InvokeFunction| Worker
    Renderer -->|logs + EMF metrics| CW
    Orchestrator -->|logs + EMF metrics| CW
    Worker -->|logs + EMF metrics| CW
    GHA -->|s3 sync + CloudFront invalidation| S3
    GHA -->|lambda update-function-code| Renderer
    GHA -->|lambda update-function-code| Orchestrator
    GHA -->|lambda update-function-code| Worker
Loading

Render Paths

Path Endpoint Description
Single GET /render One Lambda renders the full image using goroutine work blocks
Distributed GET /render-distributed Orchestrator fans out to worker Lambdas by tile and assembles the PNG

Both public paths return PNG. Raw RGBA is used internally between the orchestrator and worker.

Key Design Decisions

  • arm64 Lambdas for better price/performance on numeric workloads.
  • Single render path as a baseline so distributed rendering has something measurable to compare against.
  • Direct Lambda fan-out for the distributed path to minimize orchestration latency.
  • PNG encoding at the Lambda boundary to reduce API Gateway payload size and browser decode work.
  • CloudFront + private S3 for secure static frontend hosting.
  • Terraform-managed infrastructure with an S3 remote state backend.
  • GitHub Actions OIDC for deployment without long-lived AWS credentials.

Repository Layout

backend/               Go Lambda renderer, orchestrator, and worker
frontend/fractal-app/  React/Vite + Chakra UI 3 Mandelbrot explorer
infra/terraform/       AWS app infrastructure
infra/terraform-state/ Terraform remote state bootstrap
scripts/               Build, package, deploy, and inventory helpers
docs/                  Architecture, observability, and CI/CD notes
.github/workflows/     CI plus frontend/backend deployment workflows

Generated AWS inventory files under docs/aws-*.md are ignored because they can contain account IDs, ARNs, resource names, and topology details.

Requirements

  • Go 1.26.4
  • Node.js 24 + npm
  • Terraform >= 1.9
  • AWS CLI with an authenticated profile
  • A custom domain for the frontend
  • An issued ACM certificate matching that domain

CloudFront viewer certificates must be in us-east-1, even when the rest of the stack runs in another AWS region.

Configuration Files

Create local config from examples:

cp .env.example .env
cp infra/terraform/terraform.tfvars.example infra/terraform/terraform.tfvars

Set real values in those local files:

Local value Where Purpose
AWS_PROFILE .env, terraform.tfvars AWS CLI profile for local commands
AWS_REGION / aws_region .env, terraform.tfvars Region for Lambda, API Gateway, S3, and logs
ACM_REGION / acm_region .env, terraform.tfvars Region for the CloudFront certificate, usually us-east-1
FRONTEND_DOMAIN / frontend_domain .env, terraform.tfvars Public frontend hostname, for example mandelbrot.example.com
CERTIFICATE_DOMAIN / certificate_domain .env, terraform.tfvars ACM certificate lookup name, for example *.example.com
GITHUB_ORG / github_org .env, terraform.tfvars GitHub user or organization allowed to assume the deploy role
GITHUB_REPO / github_repo .env, terraform.tfvars Repository allowed to assume the deploy role

Frontend-only local values live in frontend/fractal-app/.env.example.

Common Commands

make help

make backend-test
make backend-package

make frontend-install
make frontend-lint
make frontend-build

make tf-init
make tf-plan
make tf-apply
make tf-output

The root Makefile reads values from environment variables or command-line overrides. If you keep local values in .env, export them before running Make targets:

set -a
source .env
set +a

Local Development

Run the local backend. It exposes both GET /render and GET /render-distributed:

make backend-local

Smoke-test the local backend:

make local-api-smoke

Run the frontend against the local backend:

make frontend-local
# open http://127.0.0.1:5173

Deploy

See docs/cicd.md for the full CI/CD setup. Short local bootstrap:

# Build the Lambda package used by Terraform on first apply.
make backend-package

# Bootstrap remote Terraform state once per AWS account/environment.
make tf-state-init
make tf-state-apply
make tf-write-backend-config

# Initialize and apply the application stack.
make tf-init
make tf-plan
make tf-apply

# Build and upload frontend assets manually if needed.
make frontend-build
make deploy-frontend \
  FRONTEND_BUCKET=$(terraform -chdir=infra/terraform output -raw frontend_bucket_name) \
  DISTRIBUTION_ID=$(terraform -chdir=infra/terraform output -raw cloudfront_distribution_id)

After GitHub Actions is configured, pushes to main can deploy frontend and backend application code automatically.

GitHub Actions Configuration

Create these repository or environment variables in GitHub Actions:

Variable Value
AWS_REGION Same value as Terraform aws_region
FRONTEND_BUCKET terraform output frontend_bucket_name
CLOUDFRONT_DISTRIBUTION_ID terraform output cloudfront_distribution_id
VITE_API_URL terraform output api_url
VITE_DISTRIBUTED_API_URL terraform output distributed_api_url
RENDERER_FUNCTION_NAME terraform output lambda_function_name
WORKER_FUNCTION_NAME terraform output worker_lambda_function_name
ORCHESTRATOR_FUNCTION_NAME terraform output orchestrator_lambda_function_name

Create this GitHub Actions secret:

Secret Value
AWS_DEPLOY_ROLE_ARN terraform output github_actions_deploy_role_arn

No GitHub secret should contain AWS access keys. The deploy workflows assume the Terraform-managed role through GitHub OIDC.

Verify

curl -o /tmp/fractal.png \
  "$(terraform -chdir=infra/terraform output -raw api_url)?width=64&height_px=64&samples=1&maxIter=100"
file /tmp/fractal.png

curl -o /tmp/fractal-dist.png \
  "$(terraform -chdir=infra/terraform output -raw distributed_api_url)?width=64&height_px=64&samples=1&maxIter=100&tileSize=400"
file /tmp/fractal-dist.png

Observability

The Go Lambda emits structured JSON logs and CloudWatch metrics via AWS Embedded Metric Format. Metrics emitted per request include:

  • RenderDurationMs
  • RenderSuccess
  • RenderFailure
  • RenderValidationFailure

Log retention is controlled by var.log_retention_days and defaults to 1 day for cost control. See docs/observability.md.

Cleanup

make tf-destroy

The ACM certificate is a data source and is not destroyed by this stack.

Credits

Original Go Mandelbrot idea adapted from parallel-mandelbrot-go by Daniela Ferreira, then rebuilt as a serverless AWS Lambda application.

About

An interactive Mandelbrot explorer built as a cloud/platform engineering project. The backend renders fractals on demand with Go Lambda functions, the frontend is a React/Vite app delivered through CloudFront and private S3, and the infrastructure is managed with Terraform.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages