mirror of
https://github.com/paperclipai/paperclip
synced 2026-05-12 18:08:04 +02:00
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies and ships
a
> "local-first, cloud-ready" deployment model
> - The deploy docs currently cover local/Docker but not a production
> cloud target, so teams asking "how do I put this behind a real domain"
> have no canonical path
> - We already support Docker images, RDS-compatible Postgres, and an
EFS
> storage profile, so AWS ECS Fargate is a natural fit
> - Without a runbook, each team reinvents VPC, security groups, TLS,
and
> secrets wiring and usually gets at least one step wrong
> - This pull request adds `docs/deploy/aws-ecs.md`, an ECS
task-definition
> template, and an `.env.aws.example`, cross-linked from the deploy
overview
> - The benefit is a single, reproducible ~$110/mo path to a production
> deployment, plus a full teardown for throwaway environments
## What Changed
- New `docs/deploy/aws-ecs.md` — an 11-step ECS Fargate runbook covering
ECR,
VPC, RDS, EFS, Secrets Manager, IAM, ALB, and ECS service with the
deployment circuit breaker enabled
- New `docker/ecs-task-definition.json` — Fargate-ready task definition
with
`<ACCOUNT_ID>`, `<REGION>`, `<EFS_ID>`, `<DOMAIN>` placeholder tokens
- New `docker/.env.aws.example` — documents every non-secret env var the
ECS deployment needs
- `docs/deploy/overview.md` — one-line cross-reference to the new guide
- Greptile feedback addressed in follow-up commits:
- `containerName` in the service-create call now matches
`paperclip-server` in the task definition
- HTTP :80 listener added that 301-redirects to :443
- Dedicated RDS DB subnet group created before `create-db-instance`
- EFS teardown polls on mount-target deletion instead of `sleep 30`
## Verification
- Walked every step of the runbook against the task definition to
confirm
variable names (`$ALB_SG`, `$ECS_SG`, `$RDS_SG`, `$EFS_SG`, `$TG_ARN`,
`$LISTENER_ARN`, `$HTTP_LISTENER_ARN`, `$EFS_ID`, `$RDS_ENDPOINT`, etc.)
are
defined before they are referenced
- Confirmed the `containerName` in Step 10 (`paperclip-server`) matches
`docker/ecs-task-definition.json` line 11
- Confirmed the `sed` placeholder substitution in Step 8 matches the
tokens
in the task definition template
- Teardown order was checked in reverse-dependency order: ECS service →
listeners → target group → ALB → RDS (waits for deletion) → DB subnet
group → EFS mount targets (polled) → EFS → secrets → SGs → ECR → IAM →
log group
## Risks
- **Low risk for the repo.** Docs-only change plus two template files
under
`docker/`; no runtime code paths are touched and nothing is imported by
the build.
- **Risk for users who follow the runbook:** AWS bills accrue
immediately
once RDS/ALB/EFS exist. The runbook calls this out and includes a full
teardown procedure. Placeholder tokens (`<ACCOUNT_ID>`, `<REGION>`,
`<EFS_ID>`, `<DOMAIN>`) are documented so nothing is silently
hard-coded.
## Model Used
- Claude (Anthropic), model `claude-opus-4-6`, ~200K context window,
extended thinking mode on, used with tool access (file edit, shell) via
Claude Code. The Greptile follow-up commits were authored the same way.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have run tests locally and they pass — N/A for docs/config
templates; validated by reading
- [x] I have added or updated tests where applicable — N/A for docs
- [x] If this change affects the UI, I have included before/after
screenshots — N/A, no UI
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
91 lines
3.0 KiB
JSON
91 lines
3.0 KiB
JSON
{
|
|
"family": "paperclip-server",
|
|
"networkMode": "awsvpc",
|
|
"requiresCompatibilities": ["FARGATE"],
|
|
"cpu": "2048",
|
|
"memory": "4096",
|
|
"executionRoleArn": "arn:aws:iam::<ACCOUNT_ID>:role/paperclip-ecs-execution",
|
|
"taskRoleArn": "arn:aws:iam::<ACCOUNT_ID>:role/paperclip-ecs-task",
|
|
"containerDefinitions": [
|
|
{
|
|
"name": "paperclip-server",
|
|
"image": "<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/paperclip-server:latest",
|
|
"essential": true,
|
|
"portMappings": [
|
|
{
|
|
"containerPort": 3100,
|
|
"protocol": "tcp"
|
|
}
|
|
],
|
|
"environment": [
|
|
{ "name": "NODE_ENV", "value": "production" },
|
|
{ "name": "HOST", "value": "0.0.0.0" },
|
|
{ "name": "PORT", "value": "3100" },
|
|
{ "name": "SERVE_UI", "value": "true" },
|
|
{ "name": "PAPERCLIP_HOME", "value": "/paperclip" },
|
|
{ "name": "PAPERCLIP_INSTANCE_ID", "value": "default" },
|
|
{ "name": "PAPERCLIP_CONFIG", "value": "/paperclip/instances/default/config.json" },
|
|
{ "name": "PAPERCLIP_DEPLOYMENT_MODE", "value": "authenticated" },
|
|
{ "name": "PAPERCLIP_DEPLOYMENT_EXPOSURE", "value": "public" },
|
|
{ "name": "PAPERCLIP_PUBLIC_URL", "value": "https://<DOMAIN>" },
|
|
{ "name": "PAPERCLIP_MIGRATION_AUTO_APPLY", "value": "true" },
|
|
{ "name": "HEARTBEAT_SCHEDULER_ENABLED", "value": "true" }
|
|
],
|
|
"secrets": [
|
|
{
|
|
"name": "DATABASE_URL",
|
|
"valueFrom": "arn:aws:secretsmanager:<REGION>:<ACCOUNT_ID>:secret:paperclip/database-url"
|
|
},
|
|
{
|
|
"name": "BETTER_AUTH_SECRET",
|
|
"valueFrom": "arn:aws:secretsmanager:<REGION>:<ACCOUNT_ID>:secret:paperclip/better-auth-secret"
|
|
},
|
|
{
|
|
"name": "ANTHROPIC_API_KEY",
|
|
"valueFrom": "arn:aws:secretsmanager:<REGION>:<ACCOUNT_ID>:secret:paperclip/anthropic-api-key"
|
|
},
|
|
{
|
|
"name": "OPENAI_API_KEY",
|
|
"valueFrom": "arn:aws:secretsmanager:<REGION>:<ACCOUNT_ID>:secret:paperclip/openai-api-key"
|
|
},
|
|
{
|
|
"name": "GITHUB_TOKEN",
|
|
"valueFrom": "arn:aws:secretsmanager:<REGION>:<ACCOUNT_ID>:secret:paperclip/github-token"
|
|
}
|
|
],
|
|
"mountPoints": [
|
|
{
|
|
"sourceVolume": "paperclip-data",
|
|
"containerPath": "/paperclip",
|
|
"readOnly": false
|
|
}
|
|
],
|
|
"healthCheck": {
|
|
"command": ["CMD-SHELL", "curl -f http://localhost:3100/api/health || exit 1"],
|
|
"interval": 30,
|
|
"timeout": 5,
|
|
"retries": 3,
|
|
"startPeriod": 60
|
|
},
|
|
"logConfiguration": {
|
|
"logDriver": "awslogs",
|
|
"options": {
|
|
"awslogs-group": "/ecs/paperclip",
|
|
"awslogs-region": "<REGION>",
|
|
"awslogs-stream-prefix": "server"
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"volumes": [
|
|
{
|
|
"name": "paperclip-data",
|
|
"efsVolumeConfiguration": {
|
|
"fileSystemId": "<EFS_ID>",
|
|
"rootDirectory": "/",
|
|
"transitEncryption": "ENABLED"
|
|
}
|
|
}
|
|
]
|
|
}
|