Skip to content

RDS Proxy target stuck in PENDING_PROXY_CAPACITY in two VPCs, works in a third — anything left to check?

0

RDS Proxy targets never reach AVAILABLE in two of my three VPCs. They sit in UNAVAILABLE / PENDING_PROXY_CAPACITY for around 30 minutes, then flip to:

"TargetHealth": {
    "State": "UNAVAILABLE",
    "Description": "DBProxy Target unavailable due to an internal error"
}

Role stays UNKNOWN the whole time — it never resolves to READ_WRITE.

In the third VPC, a proxy with the same settings reaches AVAILABLE in 4-7 minutes.

In every case the proxy resource itself reaches available and its ENIs are created and reach in-use, one per AZ. Only the target registration never completes.

The part I can't explain

The three VPCs are copies of the same template, one per environment. I created a proxy in the working VPC as a control at the same time one in a failing VPC was stuck — a proxy created at 14:25 came up AVAILABLE while one created at 13:57 the same day was still pending. So the two overlapped in the same minutes, in the same account and region. That seems to rule out a transient regional condition.

Engine is PostgreSQL 16.13 on db.t3.micro, Multi-AZ, in all three environments.

What I've already ruled out

  • Infrastructure-as-code: reproduced by creating a proxy with aws rds create-db-proxy from the CLI, different name, reusing the same secret and IAM role. Same failure.
  • Transient: five attempts across two days in the failing VPCs, plus the overlapping control above.
  • Proxy configuration: describe-db-proxies output is field-for-field identical between a working and a failing proxy — EngineFamily POSTGRESQL, RequireTLS true, IdleClientTimeout 1800, DebugLogging false, Auth SECRETS with IAMAuth DISABLED and ClientPasswordAuthType POSTGRES_SCRAM_SHA_256.
  • Connection pool config: target group reports available.
  • Subnet IP exhaustion: 244-250 free addresses in every subnet used, in all three VPCs.
  • ENI provisioning: succeeds in the failing VPCs too, one ENI per AZ, all in-use.
  • Security groups: the group allows TCP 5432 from itself and permits egress on 5432 plus all traffic. Identical rules in all three VPCs.
  • Network ACLs: all three use the default ACL — allow all, both directions.
  • VPC DNS: enableDnsSupport and enableDnsHostnames are true in all three.
  • Reaching Secrets Manager: every proxy subnet in all three VPCs is associated with a route table whose 0.0.0.0/0 route points at that VPC's NAT gateway. None of the VPCs has a Secrets Manager interface endpoint, so all three reach it identically.
  • Database instance: same class, same engine version, available, IAM database authentication disabled in all three.
  • Availability zones: all three VPCs place the proxy in the same three physical AZs, by AZ ID (use2-az1, use2-az2, use2-az3), with identical subnet CIDR sizes.

Questions

  1. What does PENDING_PROXY_CAPACITY actually wait on, beyond subnet IPs and ENIs? Both are fine in the failing VPCs, and the proxy in a sibling VPC scales in minutes.
  2. Is there a VPC-scoped limit, reservation, or internal state relevant to RDS Proxy that isn't visible through describe-db-proxies / describe-db-proxy-targets?
  3. Is there anything else worth checking before this becomes a support case?
2 Answers
0
Accepted Answer

Resolved — root cause was an expired CA certificate on the DB instance, not proxy capacity

Closing the loop on this, because the failure mode had nothing to do with what the error message says.

Root cause

Both failing DB instances were still presenting rds-ca-2019, which expired on 2024-08-22. The instance in the working VPC had already been rotated to rds-ca-rsa2048-g1. That was the only difference between the three environments — everything else really was identical, which is why the comparison never surfaced it.

RDS Proxy connects to the backend over TLS and validates the server certificate. With an expired CA the handshake never completes, so the proxy never establishes a backend connection and target registration never finishes. Role staying UNKNOWN is consistent with that: target discovery never runs.

It stayed hidden for two years because our application connects with libpq's default sslmode=prefer — TLS without certificate verification. An expired CA is invisible to a non-verifying client. The proxy was the first verifying client we ever pointed at these instances.

Fix

aws rds modify-db-instance \
  --db-instance-identifier <instance> \
  --ca-certificate-identifier rds-ca-rsa2048-g1 \
  --apply-immediately

No restart was required (the console's "Restart required: No" was accurate). The instance returned to available in about 75 seconds, and the proxy target went to AVAILABLE / READ_WRITE roughly 30 seconds after that — after 30+ minutes of PENDING_PROXY_CAPACITY on every previous attempt. Production served HTTP 200 on its health and API endpoints throughout the rotation.

Both previously failing environments are now healthy:

insurance-flint-proxy-staging     READ_WRITE  AVAILABLE
insurance-flint-proxy-production  READ_WRITE  AVAILABLE

Worth noting for anyone who lands here: rds-ca-2019 is no longer offered by describe-certificates, so this rotation is one-way.

The one-line check I wish I had run first

aws rds describe-db-instances --db-instance-identifier <instance> \
  --query 'DBInstances[0].[CACertificateIdentifier,CertificateDetails.ValidTill]'

That would have found it in seconds. Instead the PENDING_PROXY_CAPACITY wording sent us through subnet free IPs, ENIs, security groups, network ACLs, VPC DNS attributes, route tables and NAT, instance class and engine version, proxy configuration field by field, RDS-EVENT-0243, AZ IDs, service quotas, CLI-vs-Terraform creation, and a control proxy in the working VPC. All identical or ruled out.

What actually pointed at it was the RDS console's "Databases requiring certificate update" banner, which had been flagging both instances the entire time and which we had not connected to the proxy failure.

Feedback / remaining questions

  1. The reported reason is misleading. A backend TLS handshake failure is surfaced as PENDING_PROXY_CAPACITY — "DBProxy Target is waiting for proxy to scale to desired capacity" — and then as a generic "unavailable due to an internal error". Neither mentions TLS or the certificate. Is there a more specific TargetHealth.Reason that should have been returned here, and if not, can one be added? An expired backend CA is a deterministic, detectable condition on your side.

  2. Nothing was emitted to point at it. describe-events --source-type db-proxy returned only creation and deletion events for every proxy, in the failing VPCs and the working one alike. Enabling proxy DebugLogging also produced nothing about the handshake. Is there any log or event surface where the backend TLS failure is visible? If there isn't, that seems like the highest-value gap here.

  3. Is the mechanism as I've described it? My conclusion is inferred from the correlation — rotating the CA fixed it immediately after five failed attempts across two days — plus the fact that the working VPC was the one already on a valid CA. Please confirm whether RDS Proxy does hard-fail backend TLS validation against the instance CA, so I can document it correctly for our team.

  4. Detection going forward. Other than the console banner and polling CertificateDetails.ValidTill, is there a supported way to be alerted on an approaching or expired instance CA — an RDS event category, an EventBridge event, or a CloudWatch metric?

We have since declared ca_cert_identifier explicitly in our Terraform module; it had never been declared, which is how the setting drifted unobserved for two years.

answered 10 days ago

EXPERT

reviewed 9 days ago

0

Hi,

Given you have already ruled out couple of things while validating at your end, i would suggest to check the following as these can also cause 'UNAVAILABLE / PENDING_PROXY_CAPACITY' state for RDS Proxy.

This issue comes when there is an issue for the RDS Proxy in order to connect with target engine/instance. Thus, please check:-

  1. Secrets Manager secret format issue - If the secret does not strictly follow the format {"username":"...","password":"..."}, the proxy cannot extract credentials.

  2. Your RDS Proxy's IAM role must be correct/having all required permission as mentioned here - https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy-iam-setup.html#rds-proxy-iam-setup-steps

  3. if the RDS Proxy monitoring user 'rdsproxyadmin' doesn't have permissions to connect to the 'postgres' database, it would lead to similar issue. You would have to grant the user 'rdsproxyadmin' the CONNECT privilege on the 'postgres' database so that the proxy can create monitoring connections on the target database and hence treat it as healthy.

GRANT CONNECT ON DATABASE postgres TO rdsproxyadmin;

This occurs because some users run below command as security best practice and that would take CONNECT privilege for RDS Proxy admin user too:-

REVOKE CONNECT ON DATABASE postgres FROM PUBLIC;

Be aware that you will also need the users mentioned on the secrets associated to proxy to be granted as above (for health check with postgres database).

AWS
SUPPORT ENGINEER

answered 10 days ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.