Skip to content

AWS deployment

For AWS users with a registered domain who want to scale beyond the local Docker self-host path. By the end, you’ll have a Lambda function triggered by S3 events processing emails into DynamoDB, plus a monthly-summary Lambda triggered on schedule by EventBridge.

Prerequisite — a registered domain. This path requires a domain you control DNS for. SES inbound depends on MX and DKIM records on that domain; without one, nothing in this guide works. If you do not already own a domain, the IMAP/Docker path (Email setup) does the same job without DNS.

Before running the deployment scripts:

  1. Two IAM roles in the AWS console. Create email-parser-iam (with AWSLambdaBasicExecutionRole plus s3:GetObject on the inbound bucket) and lambda-s3-trigger-role (used by the monthly-summary function). The deployment scripts assume both already exist.
  2. Email-to-S3 routing. Complete Email-to-S3 setup so your S3 bucket already receives bank emails. Lambda is downstream of that pipeline; without inbound emails landing in S3, the function has nothing to trigger on.

Lambda needs emails already arriving in your S3 bucket. That’s a separate setup step — domain verification, SES inbound, MX record, S3 receipt rule. See Email-to-S3 setup before running the scripts below.

Note: The Lambda Docker setup (docker/email_parsing/) is separate from the devcontainer. The devcontainer is for local development; the Lambda Docker is for AWS deployment.

Terminal window
# Build docker image for Lambda (from repo root, using -f flag)
docker build --platform linux/amd64 -f docker/email_parsing/Dockerfile -t docker-image:test .
# Or use the build script (can be run from any directory):
bash docker/email_parsing/2_build_image.sh
# Run container locally for testing
docker run -it --rm docker-image:test
# Test Lambda function with sample event
# (Ensure test_event.json is configured properly)

The OPENAI_API_KEY environment variable must be set on the Lambda function configuration. It is not baked into the Docker image.

Run the deployment scripts in order from docker/email_parsing/:

Terminal window
cd docker/email_parsing/
./0_authenticate_to_ecr.sh # Authenticate to AWS ECR
./1_aws_repo_create.sh # Create ECR repository
./2_build_image.sh # Build and tag Docker image
./3_push_image.sh # Push image to ECR
./4_establish_lambda_func.sh # Create Lambda function
./5_update_func.sh # Update existing Lambda function
./6_invoke_func.sh # Test invoke Lambda function
./7_establish_summary_lambda.sh # Create monthly-summary Lambda function
./8_create_schedule.sh # Create EventBridge monthly schedule

For routine updates to an existing deployment, you typically only need steps 0, 2, 3, and 5.

If you want to read the AWS documentation alongside the scripts, the canonical references are:

  • Steps 0–3: Pushing a Docker image to an ECR private repository. The build script targets linux/amd64 because Lambda runs x86_64 by default; aws ecr create-repository uses --image-tag-mutability MUTABLE and --image-scanning-configuration scanOnPush=true. (Note: AWS now recommends configuring image scanning at the registry level via PutRegistryScanningConfiguration rather than per-repo with --image-scanning-configuration. The per-repo flag still works and is acceptable for a single-account hobby deployment.)
  • Step 4: Creating Lambda functions defined as container images. The script invokes aws lambda create-function --package-type Image --code ImageUri=<ECR-URI>.
  • Step 5: Updating Lambda function code with a container image. Uses aws lambda update-function-code --image-uri against both the email-parser and monthly-summary functions. (The same images-create.html page covers both create and update workflows.)
  • Step 7: Same Lambda container-image flow for the monthly-summary function. Assumes the role lambda-s3-trigger-role already exists.
  • Step 8: Creating a schedule with EventBridge Scheduler. Cron cron(0 17 8 * ? *) is “minute 0, 17:00 UTC, day-of-month 8, any month, day-of-week placeholder, any year” — the 6-field format EventBridge Scheduler requires.

The Lambda function loads OPENAI_API_KEY from SSM Parameter Store at /email-parser/openai-api-key (Working with Parameter Store). Falls back to env var, then .env for local/CI use.

Send a test email to your SES inbound address. Within ~30 seconds:

  1. CloudWatch Logs for the email-parser function should show parser output.
  2. The Transactions table in DynamoDB should have a new item with the parsed transaction.
  3. The dashboard should display the parsed transaction.

If any of those don’t happen, check CloudWatch Logs first — the Lambda function logs every step from S3 trigger through DynamoDB write.

Your Lambda is deployed. Now wire notifications: pick a provider so you get alerted when transactions arrive.