Three concurrent changes that take Fargate from "validated and idle"
to "the deploy target":
1. New ECS workflows go from workflow_dispatch only to auto-trigger:
- staging_deploy_ecs.yml: pull_request to main (mirrors the legacy
staging_deploy.yml trigger so PRs get deployed for review)
- production_deploy_ecs.yml: push to release (mirrors the legacy
production_deploy.yml trigger)
workflow_dispatch stays available on both as a manual fallback.
2. Legacy SSH-based workflows renamed to *.yml.disabled so GitHub
Actions stops running them. The files are kept (not deleted) so a
rollback during the soak window is a one-line rename:
mv production_deploy.yml.disabled production_deploy.yml
Also rename + revert the new workflow triggers, point Route53 at
the EC2 IP, and we're back. After 1+ week stable on Fargate, these
.disabled files get deleted entirely.
3. Prod logger switches to console-only:
config :logger, backends: [:console]
The global config/config.exs ships [:console, LoggerFileBackend].
The file backend made sense on EC2 where a CloudWatch agent shipped
logs/info.log; on Fargate the filesystem is ephemeral so file logs
would disappear on every task replacement. Fargate's awslogs driver
captures stdout/stderr to CloudWatch directly, so :console is all we
need. This was deferred from Phase 0 because flipping it earlier
would have broken EC2's CloudWatch shipping.
After this PR merges:
- Pushing to release deploys to Fargate (not EC2)
- PRs to main deploy to staging Fargate (not EC2)
- EC2 keeps running the pre-cutover code as a passive fallback
- DNS for db.avantifellows.org / staging-db.avantifellows.org
already points to the ALB (Phase 1 Route53 records), so the cut is
live the moment Fargate has healthy targets
Depends on Phase 4 PR #500 (this branch is stacked on it).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Phase 5 (cutover) of the EC2 → ECS Fargate migration. Three coordinated changes that take Fargate from "validated and idle" to "the actual deploy target":
workflow_dispatchonly to auto-trigger.yml.disabledThis PR is stacked on top of #500 (Phase 4) so the workflow trigger edits target the new files. Once Phase 4 merges to main, the GitHub PR base will resolve to main automatically.
Before this PR merges, all of these must be true:
terraform applyhas run for both environments (staging and prod)DB_SERVICE_AWS_ACCESS_KEY_ID/DB_SERVICE_AWS_SECRET_ACCESS_KEYset in repo secrets (fromterraform applyoutputs withcreate_ci_user = true)gh workflow run staging_deploy_ecs.ymlandgh workflow run production_deploy_ecs.ymlonce each)If any of these aren't done, don't merge. The previous PRs are reversible by themselves; this one is the point where deploys flip over.
Change details
1. Workflow trigger flip
staging_deploy_ecs.ymlworkflow_dispatchonlypull_request: branches: [main]+workflow_dispatchproduction_deploy_ecs.ymlworkflow_dispatchonlypush: branches: [release]+workflow_dispatchMirrors the legacy workflows' triggers exactly, so the muscle memory ("PR to main = staging deploy", "push to release = prod deploy") still holds.
2. Legacy SSH workflow renames
GitHub Actions only reads
.yml/.yamlfiles, so these are inert. Files kept (not deleted) so the rollback isgit mv X.yml.disabled X.yml.ci.yml(lint/dialyzer/credo, no deploy) stays unchanged.3. Prod logger to console-only
In
config/prod.exs:The global
config/config.exsships both:consoleandLoggerFileBackend. On EC2 the file backend wrote tologs/info.log, which a CloudWatch agent shipped. On Fargate the file would disappear on every task replacement — and theawslogsdriver is already capturing stdout. This change was deliberately deferred from Phase 0 because flipping it earlier would have broken EC2's log shipping during the migration.Rollback procedure (during the 1-week soak)
If Fargate misbehaves and you need to put EC2 back in the path:
db.avantifellows.org(and/orstaging-db.avantifellows.org) from the ALB alias to the EC2 instance's public IP. (Or destroy the Route53 record from Terraform and let the old DNS-by-IP setup take over — depends on what was there before.)git mv .github/workflows/production_deploy.yml.disabled \ .github/workflows/production_deploy.yml git mv .github/workflows/staging_deploy.yml.disabled \ .github/workflows/staging_deploy.yml git mv .github/workflows/run_migrations.yml.disabled \ .github/workflows/run_migrations.yml_ecs.ymltrigger changes back toworkflow_dispatch-only so they don't fight the SSH workflows.config/prod.exschange so the next EC2 deploy gets the file backend back.logs/info.logcontinues being written and shipped by the existing CloudWatch agent.The EC2 instance is the safety net; it stays running until step 12 of the design doc's order of operations ("After 1+ week stable: decommission EC2, delete old workflows").
What's NOT in this PR
These are operational steps, not code:
terraform applyfor the first timeThese are also deferred to a future cleanup PR (after the 1-week soak):
*.yml.disabledfilesLoggerFileBackenddep frommix.exs(only used by the now-disabled file backend in EC2)Test plan
actionlintpasses on both modified workflow filesconfig/prod.exsso dev (config/dev.exs) is unaffectedstaging_deploy_ecs.ymlruns automatically andstaging_deploy.yml.disableddoes NOTproduction_deploy_ecs.ymlruns automatically andproduction_deploy.yml.disableddoes NOT/ecs/db-service-prodreceives the application logs (no file backend writes)🤖 Generated with Claude Code