Skip to content

feat(aws-cloudformation): production-hardened ECS Fargate templates (dedicated webhook + HA tiers) - #169

Merged
krider2010 merged 17 commits into
n8n-io:mainfrom
buddy-n8n:main
Jul 17, 2026
Merged

feat(aws-cloudformation): production-hardened ECS Fargate templates (dedicated webhook + HA tiers)#169
krider2010 merged 17 commits into
n8n-io:mainfrom
buddy-n8n:main

Conversation

@buddy-n8n

Copy link
Copy Markdown
Contributor

Summary

This adds a production-hardened ECS Fargate deployment path for n8n in queue mode, as a tier ladder of CloudFormation templates. It builds on the existing n8n-w-multimain-queuemode.yaml example: that template gets tier-agnostic reliability hardening, and two new opt-in variants add a dedicated webhook tier and a high-availability tier.

All templates are cfn-lint clean (exit 0) and were validated against load and resilience testing on live ECS Fargate deploys.

Which template

Template Use when EKS-module equivalent
n8n-w-multimain-queuemode.yaml (modified) Dev / small / cost-sensitive. Multi-main + queue mode, single RDS. Small
n8n-w-multimain-queuemode-webhooks.yaml (new) Production with real webhook load. Dedicated webhook tier + queue-depth/request-rate autoscaling + hardening. Medium
n8n-w-multimain-queuemode-webhooks-ha.yaml (new) Failover-sensitive production. Aurora (writer + reader, ~6s failover vs ~3 min single-RDS) + Redis Multi-AZ + larger tasks. Large

Reliability hardening (all templates)

  • DB connection tuning for faster RDS-failover reconnect: keep-alive on, tuned connection/idle timeouts. STATEMENT_TIMEOUT left at the n8n default to avoid failing long migrations.
  • Graceful shutdown + ALB draining: N8N_GRACEFUL_SHUTDOWN_TIMEOUT=90, container StopTimeout=120, target-group deregistration_delay.timeout_seconds=30 so in-flight executions and HTTP requests finish on scale-in and rolling deploys.
  • Readiness-gated health checks: target groups point at /healthz/readiness (gates on DB + Redis) instead of a shallow check, plus slow_start.duration_seconds=30. Closes transient 503s when new tasks took traffic before their connections were ready.
  • Redis HA (opt-in RedisReplicasPerNodeGroup): >=1 adds a replica and enables Multi-AZ + automatic failover; 0 keeps a single node (no behaviour change on update).
  • Correctness: pin RDS EngineVersion: "16.9" (was deprecated 16), resolve cfn-lint warnings, and keep the auto-generated (globally-unique) S3 binary bucket name.
  • DB sizing guidance embedded in the DBInstanceClass parameter description.

Dedicated webhook + HA templates add

  • Dedicated webhook tier: separate target group + listener rule routing /webhook/*, /webhook-waiting/*, /form/*, /form-waiting/* to a n8n webhook service (UI / REST / /webhook-test/* stay on the mains). N8N_DISABLE_PRODUCTION_MAIN_PROCESS=true on mains. New N8nWebhookServiceUrl output.
  • Queue-depth worker autoscaling: a 1-minute in-VPC Lambda publishes a WorkerBacklogPerTask metric from the Bull backlog key; a target-tracking policy holds backlog-per-worker at target (ECS analog of the EKS KEDA queue scaler). CPU/memory policies stay as a safety net.
  • Responsive webhook autoscaling: primary signal ALBRequestCountPerTarget (tuned via WebhookRequestsPerTask), with a sensible WebhookMinTasks floor to absorb cold bursts during scale-out lag.
  • Execution-data pruning: ExecutionsDataMaxAge / ExecutionsDataMaxCount / ConcurrencyProductionLimit.
  • Opt-in Python task runner (EnablePythonTaskRunner, default false): external runner mode + n8nio/runners sidecar + a root init container that patches the import allowlist from PythonStdlibAllow / PythonExternalAllow (default empty = no imports). No sidecar / no cost when off. Validated end-to-end on a live deploy.
  • The HA template additionally uses Aurora PostgreSQL (writer + reader), Redis Multi-AZ, higher autoscaling floors, and larger task sizes.

Base template: deliberately excluded

  • N8N_DISABLE_PRODUCTION_MAIN_PROCESS is not set (no webhook tier, so mains must keep serving production webhooks).
  • Queue-depth worker autoscaling and the dedicated webhook tier stay in the variants; the base keeps CPU/memory worker autoscaling with a pointer to the variants (documented in the README).

Testing

  • cfn-lint exit 0 on all three templates.
  • Load-tested (worker queue-depth scaling drains backlog under sustained webhook load; webhook tier scales on request rate).
  • Resilience-tested (forced RDS/Aurora failover, task-crash self-heal, rolling-deploy drain, Redis failover).
  • Python task runner validated end-to-end on a live ECS Fargate deploy.

Note: these are beta (0.x) templates validated in a single AWS account/region; some throughput figures used a trivial workflow and the HA-tier band is partly inferred from base-tier load tests. Happy to adjust naming, defaults, or scope to fit the repo's conventions.

buddy-n8n and others added 10 commits June 4, 2026 13:41
…hook service

Add n8n-w-multimain-queuemode-webhooks.yaml, a variant of the multi-main
queue-mode ECS Fargate template that breaks production webhook handling out
into its own service.

- new webhook target group, listener rule (routes /webhook/*, /webhook-waiting/*,
  /form/* and /form-waiting/* to the webhook service; UI, REST API and
  /webhook-test/* stay on the mains)
- new webhook task definition (runs `n8n webhook`) plus its service and
  CPU/memory autoscaling, mirroring the worker tier
- set N8N_DISABLE_PRODUCTION_MAIN_PROCESS=true on the mains so production
  webhooks are served only by the webhook service
- N8nWebhookServiceUrl output

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- pin RDS EngineVersion to 16.9 (bare "16" is deprecated for new instances, W3691)
- drop redundant DependsOn entries already implied by Ref/GetAtt (W3005)
- suppress W1011 on PostgresDB: MasterUserPassword is a user-supplied stack
  parameter by design

cfn-lint now passes with no errors or warnings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…late

Brings the tier-agnostic hardening from the webhooks template into the base
n8n-w-multimain-queuemode.yaml, without the dedicated webhook tier (which
remains the reason to use the -webhooks variant).

- pin EngineVersion "16.9" (was deprecated "16"); resolve cfn-lint warnings
  (W1011 suppression on PostgresDB; remove redundant DependsOn) -> exit 0
- DB connection tuning (keep-alive + connection/idle timeouts) on main + worker
- graceful shutdown: N8N_GRACEFUL_SHUTDOWN_TIMEOUT=90 + container StopTimeout=120
- ALB connection draining + slow start on the main target group
- readiness-gated ALB health check (/healthz/readiness, gates on DB+Redis)
- Redis HA as an opt-in knob (RedisReplicasPerNodeGroup, default 0)
- DBInstanceClass sizing guidance; note pointing to the -webhooks template
- pin the n8nio/n8n image to 2.23.4 (was :latest) for reproducible deploys

Does NOT set N8N_DISABLE_PRODUCTION_MAIN_PROCESS: the base has no webhook tier,
so the main process must keep serving production webhooks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(aws-cloudformation): ECS Fargate template with dedicated webhook service
…-hardening

# Conflicts:
#	aws-cloudformation/ecs-fargate/n8n-w-multimain-queuemode.yaml
feat(aws-cloudformation): backport reliability hardening to base multimain-queuemode template
… main

The full ECS Fargate work was committed to the ecs-fargate-0.1.1 release
tag only and never brought forward to main. This ports the missing
deliverables onto main:

- Add n8n-w-multimain-queuemode-webhooks-ha.yaml (HA/scale tier template).
- Update n8n-w-multimain-queuemode-webhooks.yaml to the complete release
  version (queue-depth worker autoscaling, request-rate webhook autoscaling,
  DB failover tuning, graceful shutdown + ALB draining, Redis HA,
  readiness-gated health checks, execution-data pruning, opt-in Python task
  runner).
- Add ecs-fargate/RELEASE.md.

The base template (n8n-w-multimain-queuemode.yaml) is left as-is: main's
version already supersedes the tag's (ECS scaling params + the n8n-io#164 S3
fix).

The tag was cut from upstream 1.9.0, before fix n8n-io#164, so its -webhooks and
-ha templates carried the collision-prone explicit S3 BucketName. That fix
is reapplied here so both templates auto-generate a globally-unique bucket
name, matching the base template. cfn-lint exit 0 on all three templates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
….1.1

feat(aws-cloudformation): forward-port ecs-fargate 0.1.1 templates to main
Remove the release-note file and fold its still-useful content into the
README so there is a single source of truth for the ecs-fargate templates.

- Add a "Which template to use" tier table (base / -webhooks / -ha).
- Fix the stale Queue-Depth Scaling section: the -webhooks and -ha
  templates now implement queue-depth worker autoscaling directly (in-VPC
  Lambda + WorkerBacklogPerTask target tracking); the base template stays
  CPU/memory only.
- Note the dedicated webhook tier and the Enterprise-license requirement.
- Warm up the intro.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the dig at Kubernetes/control-plane cost; frame ECS Fargate as a
serverless-container option alongside the EKS module instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 1 file (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread aws-cloudformation/ecs-fargate/README.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

Comment thread aws-cloudformation/ecs-fargate/n8n-w-multimain-queuemode-webhooks.yaml Outdated
Comment thread aws-cloudformation/ecs-fargate/n8n-w-multimain-queuemode-webhooks.yaml Outdated
Comment thread aws-cloudformation/ecs-fargate/n8n-w-multimain-queuemode-webhooks.yaml Outdated
Comment thread aws-cloudformation/ecs-fargate/n8n-w-multimain-queuemode-webhooks-ha.yaml Outdated
Comment thread aws-cloudformation/ecs-fargate/n8n-w-multimain-queuemode-webhooks.yaml Outdated
buddy-n8n and others added 5 commits July 14, 2026 09:08
…tely

Address cubic review: the intro referenced an unnamed, unlinked "EKS
module". Point readers at the actual paths instead: the in-repo Helm chart
(charts/n8n) and the terraform-aws-n8n EKS module, and link that module
from the tier-parity table.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
n8n 2.x does not load DB_POSTGRESDB_SSL_CA as a file path, so the RDS CA
bundle downloaded by the rds-cert-fetcher init container was never trusted.
With DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED left at its secure default the
tasks crash on DB init with "self-signed certificate in certificate chain";
the templates masked that by disabling verification (REJECT_UNAUTHORIZED
false), which connects to Postgres without validating the server cert.

Point NODE_EXTRA_CA_CERTS at the same bundle instead (node trusts it
natively, and it also covers the ElastiCache Redis TLS chain), and set
DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED to true so the RDS certificate is
actually verified. Applies to the main, worker, and webhook task
definitions across all three templates. The rds-cert-fetcher init
container, /certs volume, and DB_POSTGRESDB_SSL_ENABLED are unchanged.

Validated against live RDS: tasks reach "Migrations in progress", stacks
reach CREATE_COMPLETE, /healthz serves 200 over HTTPS, and workflows
execute and autoscale under load.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(aws-cloudformation): verify RDS TLS via NODE_EXTRA_CA_CERTS
Follow-up to the RDS TLS fix, covering the other five cubic findings across
all three ecs-fargate templates.

- Stack-scope Secrets Manager names (${AWS::StackName}-app-user/*) and every
  name-based ARN reference, so tier variants can coexist in one account. [P1]
- Worker-scaler Lambda: drop the CERT_NONE / check_hostname bypass so the
  Redis TLS peer certificate is verified against the default trust store. [P1]
- Remove the weak default DB and Redis passwords so a value must be supplied,
  and update the now-stale parameter descriptions. [P2]
- Add DependsOn: VPCGatewayAttachment to the NAT gateways (create-order race). [P2]
- HA tier: add a third NAT gateway + public subnet + per-AZ route table so
  PrivateSubnet3 no longer routes egress through AZ1's NAT (cross-AZ SPOF). [P2]

Validated locally: cfn-lint exit 0 and aws cloudformation validate-template
pass on all three templates. Runtime TLS behavior (Redis) to be confirmed on
a live deploy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(aws-cloudformation): address remaining cubic review findings (secret scoping, Redis TLS, NAT HA, weak defaults)

@gml3ff gml3ff left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've discussed this already and it sounds like you've been able to successfully deploy this already, I'll merge and provide some additional updates shortly after.

@krider2010
krider2010 self-requested a review July 16, 2026 10:22

@krider2010 krider2010 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice PR — the tier-ladder framing is clear, and I like how much of the hardening is opt-in (Redis HA, the Python runner, the readiness gating). A couple of things that caught my eye too: the SG rules split out to sidestep the circular refs, and the per-AZ NAT on the HA tier.

One thing I'd want fixed before merge, plus a handful of smaller inline notes.

The blocker: queue-depth autoscaling reads the wrong Redis key, so it silently never scales. The Lambda does LLEN bull:jobs:wait, but n8n's Bull queue is default — our own KEDA examples and this PR's README both use bull:default:wait. Worth an end-to-end check after fixing, since the metric would've read a flat 0 during load testing and CPU/mem scaling might have masked it.

Rest are inline and lower stakes — a webhook health-check path I'd double-check, an S3 endpoint that misses the third AZ on the HA tier, a hardcoded worker count, the Aurora engine version, and an upgrade-path note for the base template. None of those block the concept, just polish. Details inline.

s = ctx.wrap_socket(raw, server_hostname=host)
try:
_resp(s, "AUTH", token)
return int(_resp(s, "LLEN", "bull:jobs:wait"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bull:jobs:wait won't exist — the queue is named default, so this wants bull:default:wait. Compare charts/n8n/examples/keda-autoscaling.yaml and the README in this same PR, both of which use default. As-is LLEN returns 0 every minute and the backlog metric is always 0, so the target-tracking policy never fires and you're silently back on CPU/mem-only scaling. Worth fixing the comment just above too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed in 7796403: now bull:default:wait (matching charts/n8n/examples/keda-autoscaling.yaml), and updated the comment just above too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to my earlier reply here: I switched to bull:default:wait following this repo's charts/n8n/examples/keda-autoscaling.yaml, but on verifying against n8n source that example is actually wrong. n8n names the queue jobs (QUEUE_NAME = 'jobs' in packages/cli/src/scaling/constants.ts, same at the pinned n8n@2.23.4) with default prefix bull (@n8n/config docstring example 'bull:jobs:23'), so the correct key is bull:jobs:wait, which was the PR's original value. Reverted in 9d61987, so this template is now correct. Heads up that the same bull:default:wait bug lives in charts/n8n/examples/keda-autoscaling.yaml:35, so that KEDA scaler would never fire either, worth a separate fix outside this PR.

s = ctx.wrap_socket(raw, server_hostname=host)
try:
_resp(s, "AUTH", token)
return int(_resp(s, "LLEN", "bull:jobs:wait"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bull:jobs:wait won't exist — the queue is named default, so this wants bull:default:wait. Compare charts/n8n/examples/keda-autoscaling.yaml and the README in this same PR, both of which use default. As-is LLEN returns 0 every minute and the backlog metric is always 0, so the target-tracking policy never fires and you're silently back on CPU/mem-only scaling. Worth fixing the comment just above too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, fixed in 7796403: now bull:default:wait (matching charts/n8n/examples/keda-autoscaling.yaml), and updated the comment just above too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to my earlier reply here: I switched to bull:default:wait following this repo's charts/n8n/examples/keda-autoscaling.yaml, but on verifying against n8n source that example is actually wrong. n8n names the queue jobs (QUEUE_NAME = 'jobs' in packages/cli/src/scaling/constants.ts, same at the pinned n8n@2.23.4) with default prefix bull (@n8n/config docstring example 'bull:jobs:23'), so the correct key is bull:jobs:wait, which was the PR's original value. Reverted in 9d61987, so this template is now correct. Heads up that the same bull:default:wait bug lives in charts/n8n/examples/keda-autoscaling.yaml:35, so that KEDA scaler would never fire either, worth a separate fix outside this PR.

HealthyThresholdCount: 3
UnhealthyThresholdCount: 5
# readiness (DB + Redis) gates traffic, fixes the new-task 503s during rolling deploys
HealthCheckPath: /healthz/readiness

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small inconsistency worth confirming: the target group health-checks /healthz/readiness, but the comment just above says the webhook process serves /healthz (not /), and the container's own health check curls /healthz. If the webhook process doesn't actually serve /healthz/readiness with a 200, every webhook target stays unhealthy and production /webhook/* + /form/* all 503. Can you confirm the webhook process serves readiness? If not, /healthz is the safe bet here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to /healthz in 7796403 to match the container health check, the safe consistent choice you suggested. Also fixed the comment above.

HealthyThresholdCount: 3
UnhealthyThresholdCount: 5
# readiness (DB + Redis) gates traffic, fixes the new-task 503s during rolling deploys
HealthCheckPath: /healthz/readiness

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small inconsistency worth confirming: the target group health-checks /healthz/readiness, but the comment just above says the webhook process serves /healthz (not /), and the container's own health check curls /healthz. If the webhook process doesn't actually serve /healthz/readiness with a 200, every webhook target stays unhealthy and production /webhook/* + /form/* all 503. Can you confirm the webhook process serves readiness? If not, /healthz is the safe bet here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switched to /healthz in 7796403 to match the container health check, the safe consistent choice you suggested. Also fixed the comment above.

ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
RouteTableIds:
- !Ref PrivateRouteTableAz1
- !Ref PrivateRouteTableAz2

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the HA tier this misses the third AZ — you added PrivateRouteTableAz3 for PrivateSubnet3, but the S3 endpoint only lists Az1/Az2. Workers run in all three subnets and are the ones doing S3 binary IO, so anything scheduled into AZ3 goes out via the NAT gateway instead of the free endpoint (NAT data-processing cost, and you lose the endpoint isolation). Adding !Ref PrivateRouteTableAz3 sorts it. (The -webhooks template maps subnet3 to Az1's table which is already on the endpoint, so it's only this one.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7796403: added !Ref PrivateRouteTableAz3 to the S3 gateway endpoint so AZ3 workers use the endpoint instead of the NAT gateway.

Properties:
ServiceName: !Sub "${AWS::StackName}-svc-worker"
Cluster: !GetAtt ECSCluster.Arn
DesiredCount: 4

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WorkerMinTasks is a parameter but the initial DesiredCount is hardcoded to 4, so if someone raises the floor the service still launches 4 on create and only ratchets up once autoscaling kicks in — under-provisioned right when the raised floor was meant to help. The webhook service uses !Ref WebhookMinTasks a bit further down; worth matching that with !Ref WorkerMinTasks here (same in the -webhooks template).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7796403: the worker service now uses DesiredCount: !Ref WorkerMinTasks (in both the -webhooks and -ha templates), matching the webhook service.

- W1011
Properties:
Engine: aurora-postgresql
EngineVersion: "16.9"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth double-checking 16.9 is actually an offered aurora-postgresql version — Aurora curates its minor versions separately from community RDS Postgres, so a version that's valid on the RDS-instance templates isn't guaranteed here. If it's not offered in the region, cluster creation fails with InvalidParameterCombination. Quick aws rds describe-db-engine-versions --engine aurora-postgresql confirms it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed via aws rds describe-db-engine-versions --engine aurora-postgresql: 16.9 is an offered aurora-postgresql version, so no change needed here. Thanks for the flag.

Properties:
Engine: postgres
EngineVersion: 16
EngineVersion: "16.9"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pinning these is the right move for new stacks, just flagging the upgrade path for anyone already running the old base template: bumping EngineVersion from 16 to 16.9 forces an engine modify/reboot on the single RDS instance, and if AutoMinorVersionUpgrade already moved them past 16.9 the update rolls back. Same story with latest -> 2.23.4 on the image (lines 1006/1205) — a stack that's pulled a newer n8n and migrated the DB would crash-loop on the downgrade. Probably just needs a line in the README's upgrade notes rather than a code change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, kept the pins (right for new stacks) and documented the upgrade path in the README in 7796403: added an "Upgrading existing stacks" section covering the RDS 16->16.9 engine modify/reboot (and the rollback if AutoMinorVersionUpgrade already moved past it) and the n8n image-downgrade crash-loop. No code change to the pins.

- Correct Bull backlog key to bull:default:wait (queue is named default)
  so the worker queue-depth autoscaler actually reads a non-zero LLEN.
- Point webhook target-group health check at /healthz to match the
  container health check and avoid all-webhook 503s on startup.
- Add PrivateRouteTableAz3 to the HA S3 gateway endpoint so AZ3 workers
  use the free endpoint instead of the NAT gateway.
- Use !Ref WorkerMinTasks for the worker service DesiredCount so a raised
  floor provisions on create instead of ratcheting up via autoscaling.
- Document the engine/image upgrade path in the README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 3 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread aws-cloudformation/ecs-fargate/n8n-w-multimain-queuemode-webhooks.yaml Outdated
Comment thread aws-cloudformation/ecs-fargate/n8n-w-multimain-queuemode-webhooks-ha.yaml Outdated
Comment thread aws-cloudformation/ecs-fargate/README.md Outdated

@maspio maspio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved for @gml3ff

…pgrade notes

The queue-depth scaler Lambda must read the real Bull key. n8n names the
queue `jobs` (QUEUE_NAME = 'jobs' in scaling/constants.ts) with default
prefix `bull` (config docstring example: 'bull:jobs:23'), so the key is
`bull:jobs:wait`. The earlier switch to `bull:default:wait` (following the
repo's keda-autoscaling.yaml example, which is itself wrong) would make
LLEN always return 0 and disable queue-depth autoscaling. Reverted in both
webhook templates (code + comment).

Also clarify in the README that EngineVersion and the n8n image tag are
hardcoded properties in the templates, not stack parameters, so an upgrade
means editing the template value rather than overriding a parameter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@krider2010
krider2010 self-requested a review July 17, 2026 10:55

@krider2010 krider2010 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed after the latest commits — all good from me. The webhook health-check path, the AZ3 S3 route, the worker DesiredCount, and the README upgrade notes are all sorted, and I confirmed Aurora 16.9 is a valid engine version so no change needed there.

@krider2010
krider2010 merged commit 1790f0a into n8n-io:main Jul 17, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants