pulse-aws
AWS deployment utilities for Pulse applications on ECS Fargate.
Folder Structure
src/pulse_aws/
├── __init__.py # Public exports
├── config.py # TaskConfig, HealthCheckConfig, ReaperConfig, DockerBuild
├── deployment.py # Core deployment orchestration, deploy()
├── plugin.py # AWSECSPlugin for Pulse integration
├── baseline.py # VPC, ALB, ECS cluster setup
├── certificate.py # ACM certificate management
├── teardown.py # Infrastructure cleanup
├── reporting.py # Deployment status reporting
├── reaper_lambda.py # Lambda for graceful task draining
│
├── cdk/ # AWS CDK infrastructure
│ ├── app.py # CDK app entrypoint
│ ├── baseline.py # Baseline stack definition
│ └── helpers.py # CDK utilities
│
scripts/
├── deploy.py # Deployment script
├── teardown.py # Teardown script
└── verify.py # Verification script
Features
- Zero-downtime deployments with deployment-affinity routing for HTTP and websockets
- Automatic ACM certificate management with DNS validation
- DNS configuration detection - automatically detects and guides you through DNS setup
- Baseline infrastructure as code using AWS CDK
- Multi-version support - run multiple deployments simultaneously
Quick Start
# Install
uv add pulse-aws
# Requires a working `cdk` executable on PATH.
# If you use a wrapper, pass it via --cdk-bin.
# Deploy
uv run pulse-aws deploy \
--deployment-name prod \
--domain app.example.com \
--app-file src/app/main.py \
--web-root web \
--dockerfile Dockerfile \
--context .
Architecture
See ARCHITECTURE.md for a detailed overview of:
- Infrastructure resources and how they relate
- Traffic routing with deployment affinity
- Zero-downtime deployment workflow
- Security architecture
Deployment Workflow
The deployment script orchestrates the full workflow:
1. ACM Certificate
from pulse_aws import ensure_acm_certificate
cert = await ensure_acm_certificate("api.example.com")
- Creates or retrieves an ACM certificate
- Provides DNS validation records if needed
- Waits for certificate to be ISSUED
2. Baseline Infrastructure
from pulse_aws import ensure_baseline_stack
outputs = await ensure_baseline_stack(
"prod",
certificate_arn=cert.arn,
)
Creates shared infrastructure:
- VPC with public/private subnets
- Application Load Balancer with HTTPS listener
- ECS Fargate cluster
- ECR repository
- CloudWatch log group
- Security groups
3. DNS Configuration Check
from pulse_aws import check_domain_dns
dns_config = check_domain_dns(domain, alb_dns_name)
if dns_config:
print(dns_config.format_for_display())
Automatically checks if your domain resolves to the ALB:
- ✅ Already configured: Silent success
- ✅ Proxied through Cloudflare: Treated as configured once records point to Cloudflare
- ⚠️ Not configured: Shows exact DNS record to add
Example output:
⚠️ Domain DNS Configuration Required
============================================================
🔗 Configure DNS for test.stoneware.rocks
Add the following records to your DNS provider:
• Type: CNAME
Name: test.stoneware.rocks
Value: test-alb-514905529.us-east-2.elb.amazonaws.com
(Route traffic to Application Load Balancer)
Once the records are added, your domain will be live within a few minutes.
4. Deploy Application
from pulse_aws import (
generate_deployment_id,
build_and_push_image,
register_task_definition,
create_service_and_target_group,
install_listener_rules_and_switch_traffic,
)
deployment_id = generate_deployment_id("prod")
image_uri = await build_and_push_image(...)
task_def_arn = await register_task_definition(...)
service_arn, tg_arn = await create_service_and_target_group(...)
await install_listener_rules_and_switch_traffic(...) # Waits for health checks
- Builds and pushes Docker image to ECR (with correct x86_64 architecture)
- Registers ECS task definition with IAM roles
- Creates target group and attaches to ALB listener
- Creates ECS service with 2 Fargate tasks
- Waits for targets to pass health checks (zero-downtime)
- Switches default traffic to new deployment
Zero-Downtime Deployments
Each deployment gets a unique ID (e.g., prod-20251027-122112Z):
- New deployment starts - New tasks spin up alongside old tasks
- Affinity routing - ALB creates rules for
?pulse_deployment=<deployment-id>→ target group, then uses ALBlb_cookiestickiness to keep that browser on the same ECS task inside that target group. - Default action switches - New users get new version
- Old sessions continue - Existing users stay pinned to the same deployment via prerender and Socket.IO query directives
Opening a new tab does not inherit that session affinity query, so a new tab (or any fresh session) follows the default listener action and reaches the latest deployment. 5. Drain old deployment - When ready, call drain endpoint to shut down gracefully
# Drain an old deployment
curl -X POST \
-H "Authorization: Bearer <drain-secret>" \
https://api.example.com/drain
AWSECSPlugin also exposes /_pulse/meta, which returns deployment metadata for affinity verification.
Configuration
Environment Variables
AWS_PROFILE- AWS profile to useAWS_REGION- AWS region (or set in~/.aws/config)PULSE_AWS_CDK_BIN- optional CDK executable or wrapper pathPULSE_AWS_CDK_WORKDIR- optional custom CDK app directory
Deployment Settings
result = await deploy(
domain="api.example.com",
deployment_name="prod",
docker=DockerBuild(
dockerfile_path=Path("Dockerfile"),
context_path=Path("."),
),
cdk_bin="cdk",
cdk_workdir=None,
)
- By default,
pulse-awsuses the packaged CDK app that ships insidepulse_aws/cdk. - Use
cdk_binor--cdk-binwhen you want to supply a wrapper script or alternate executable. - Use
cdk_workdiror--cdk-workdironly when you want to point deploy at a custom CDK app directory. - Host paths like
--dockerfile,--context, and--cdk-workdirresolve from the directory where you invokepulse-aws. --app-fileand--web-rootstay relative to the Docker build context.
Security
Defense in depth:
- ALB in public subnets (internet-facing)
- ECS tasks in private subnets (no direct internet access)
- NAT gateway for task outbound internet
- ALB security group: Only 80/443 from internet
- Service security group: Only 8000 from ALB
- IAM roles with least privilege
Development
# Run tests
uv run pytest packages/pulse-aws/tests/
# Deploy test environment
AWS_PROFILE=your-profile uv run packages/pulse-aws/scripts/deploy.py
Troubleshooting
Certificate validation stuck
If certificate stays in PENDING_VALIDATION:
- Check DNS validation records are added correctly
- Wait 5-10 minutes for DNS propagation
- Use
digto verify:dig _xxx.yourdomain.com CNAME
Domain not accessible after deployment
- Check DNS record points to ALB:
dig yourdomain.com - Wait for DNS propagation (can take 5-60 minutes)
- Verify ALB is healthy: Visit ALB DNS directly
Tasks failing health checks
- Check logs:
aws logs tail /aws/pulse/{env}/app --follow - Verify tasks are listening on port 8000
- Check
/_pulse/healthendpoint returns 200
License
MIT
Release files for pulse-aws 0.1.12
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pulse_aws-0.1.12.tar.gz | 44.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pulse_aws-0.1.12-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 96.9 kB
Release files / pulse_aws-0.1.12.tar.gz
| Download URL | pulse_aws-0.1.12.tar.gz |
|---|---|
| Size | 44.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c476b6bcc5c9b365dea85bba0cf973931d6ed4b18e1ab3ccfa77cbf5f850206b
|
|
BLAKE2b-256 checksum How to use checksums |
cb5192cf8908ac35e2b177618e402cbe4eeb806b17ad453f7876067ab1923992
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / pulse_aws-0.1.12-py3-none-any.whl
| Download URL | pulse_aws-0.1.12-py3-none-any.whl |
|---|---|
| Size | 52.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e36bbfd7e972c25b61aa3466a510540906742b3a42a84075b074186e478185b9
|
|
BLAKE2b-256 checksum How to use checksums |
322b36f0f3202553dbd45818e235653f808bc6e1699ea236c7ec2f0c1a19840b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.0 {"installer":{"name":"uv","version":"0.12.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|