Reaching a managed Argo CD on Amazon EKS: endpoints, identity and hub-and-spoke
The EKS Capability for Argo CD does not run in your cluster, so the usual playbook of an Ingress, a load balancer and a Dex config does not apply. This is how an enterprise actually reaches it: which endpoint people hit, how to take it off the internet, who can sign in, how pipelines authenticate, and how one instance reaches clusters in other accounts.
The managed Argo CD runs in AWS-managed infrastructure, not on your worker nodes, and it has exactly one way in: an AWS-hosted HTTPS endpoint (the serverUrl) that serves both the UI and the API. That endpoint is public by default. Enterprises make it private by creating an interface VPC endpoint for eks-capabilities and attaching it to the capability, which blocks public access and makes the same hostname resolve to private IPs that on-premises users reach over Direct Connect or VPN. People sign in only through IAM Identity Center, which can federate to Okta or Entra ID; Identity Center users and groups map to three global roles, and AppProject roles decide which Applications each team sees. Pipelines cannot use argocd login and use project tokens instead. For a fleet, one capability in a platform account reaches clusters in other accounts and Regions, even fully private ones, through an EKS Access Entry for its IAM role on each target, with no IRSA, role chaining or VPC peering. You give up Dex, local users, the notifications controller and non-EKS targets in exchange for not running Argo CD at all.
1Where the managed Argo CD actually runs
With a self-managed install, Argo CD is a set of pods in your cluster, and exposing it means an Ingress, a load balancer, a certificate and a DNS record. The EKS Capability for Argo CD changes that. AWS runs the Argo CD controllers, the repo server and the API server in its own infrastructure, outside your account. Your worker nodes never run an Argo CD pod and do not need a network path to Git or to Helm registries.
What stays in your cluster is the data. The Argo CD custom resources (Application, ApplicationSet, AppProject) and the cluster registration Secrets live in a namespace you choose when you create the capability, usually argocd. EKS installs the custom resource definitions for you, and you manage these objects with kubectl or Git like any other manifest.
- 1An engineer opens the capability's endpoint in a browser, or points the CLI at it.
- 2Argo CD, running in AWS-managed infrastructure, pulls the desired manifests from Git.
- 3It reads the Application resources through the cluster's Kubernetes API and applies the manifests the same way.
- 4The Kubernetes control plane schedules the resulting pods onto worker nodes, which run no Argo CD components.
The endpoint has the form https://{capability-name}-{hash}-{account-id}.eks-capabilities.{region}.amazonaws.com. You can set an optional endpointPrefix for a friendlier hostname. Find the URL in the EKS console (your cluster, then the Capabilities tab, then Open Argo CD UI), or with the AWS CLI once the capability is ACTIVE:
aws eks describe-capability \
--region us-east-1 \
--cluster-name platform-hub \
--capability-name argocd \
--query 'capability.configuration.argoCd.serverUrl' \
--output text
That one hostname is the whole access surface. The UI, the API, the CLI over gRPC-web and Git webhooks (${serverUrl}/api/webhook) all use it. Every access decision in the rest of this article is about who can reach that hostname, and who they are once they get there.
2The default: a public endpoint behind Identity Center
Out of the box, the endpoint is reachable from the internet. Anyone can reach the sign-in page, but nobody gets past it without an IAM Identity Center identity that you have mapped to an Argo CD role. For a proof of concept, or for an organization whose access model is "the identity provider is the perimeter", that can be enough.
- 1An engineer opens the public endpoint from any network.
- 2Argo CD redirects the browser to IAM Identity Center to sign in.
- 3Identity Center hands authentication to the corporate identity provider, where MFA and conditional access apply.
- 4A CI pipeline calls the same endpoint with a project token, not with SSO.
Two things push most enterprises past this default. Security reviews usually want deployment tooling unreachable from the internet, whatever the authentication. And the public path is also the path a stolen token would use: a token works from anywhere the endpoint is reachable.
3Private-only access from the corporate network
To take the endpoint off the internet, create an interface VPC endpoint (AWS PrivateLink) for the service com.amazonaws.{region}.eks-capabilities and attach its ID to the capability. Three things change at once:
- Public access to the Argo CD endpoint is blocked.
- AWS updates DNS so that the same hostname resolves to the private IP addresses of your VPC endpoint. You do not need a new name or a new certificate.
- Anyone who wants in needs network connectivity to those private IPs: from the VPC itself, or from networks connected to it through VPN or AWS Direct Connect.
In an enterprise, that VPC is usually a shared-services or network VPC already attached to a Transit Gateway, so on-premises users ride the existing Direct Connect or VPN path:
- 1An engineer's machine asks the corporate DNS for the Argo CD hostname.
- 2A conditional forwarder sends the query to a Route 53 Resolver inbound endpoint in the network VPC, which answers with the VPC endpoint's private IPs. This step is optional if your resolvers already get that answer.
- 3The HTTPS connection leaves the corporate network through the customer router, over Direct Connect or Site-to-Site VPN.
- 4The Transit Gateway routes it to the interface endpoint, whose security group allows port 443 from corporate address ranges.
- 5PrivateLink carries it to the capability. A request from the public internet is refused.
Steps 1 and 2 are the standard way to resolve private AWS names from on premises, not something the capability docs prescribe. Those docs say the hostname resolves to the endpoint's private IPs, and tell you to verify it with nslookup "from any network", which suggests public DNS already returns the private answer. Check what your corporate resolvers return first. If they already get the private IPs, you can skip the forwarder.
# Subnets in at least two Availability Zones.
# The security group must allow inbound 443 from every client network.
aws ec2 create-vpc-endpoint \
--region us-east-1 \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.us-east-1.eks-capabilities \
--vpc-id vpc-0a1b2c3d4e5f60718 \
--subnet-ids subnet-0aa11bb22cc33dd44 subnet-0ee55ff66aa77bb88 \
--security-group-ids sg-0123abcd4567ef890# Wait for the endpoint state to be "available" first.
aws eks update-capability \
--region us-east-1 \
--cluster-name platform-hub \
--capability-name argocd \
--configuration '{
"argoCd": {
"networkAccess": { "vpceIds": ["vpce-0123456789abcdef0"] }
}
}'# Status returns to ACTIVE when the update completes.
aws eks describe-capability --region us-east-1 \
--cluster-name platform-hub --capability-name argocd \
--query 'capability.status' --output text
# Should return RFC 1918 addresses (10.x, 172.16-31.x, 192.168.x).
nslookup argocd-a1b2c3d4-111122223333.eks-capabilities.us-east-1.amazonaws.com# An empty list restores public access.
aws eks update-capability \
--region us-east-1 \
--cluster-name platform-hub \
--capability-name argocd \
--configuration '{ "argoCd": { "networkAccess": { "vpceIds": [] } } }'A few rules from the docs shape the design:
- Private access applies at creation or later. At creation you can pass the endpoint ID in the capability configuration (networkAccess.vpceIds), or with the --network-configuration parameter shown in the CLI guide. On an existing capability, use update-capability.
- vpceIds is a list. You can attach more than one endpoint, for example one in each network VPC that users come from.
- The security group is your network control. AWS does not currently support VPC endpoint policies for the eks-capabilities service, so the endpoint's security group (source CIDRs on port 443) is the only network-level filter.
- The endpoint is now load-bearing. If you delete it, nobody can reach Argo CD until you create a new one and update the capability. Protect it like any other shared network resource.
Several Argo CD instances behind one endpoint
Large organizations rarely stop at one Argo CD. A platform team runs one for shared services, and a regulated business unit runs its own. Each instance is a separate capability on its own cluster, but they do not each need their own VPC endpoint. The endpoint is for the Region-wide eks-capabilities service, not for one capability, and each capability simply lists its ID in vpceIds.
DNS is what keeps the instances apart. Every capability has its own hostname: {capability-name}-{hash}-{account-id} by default, or {prefix}-{account-id} if you set an endpoint prefix, which must be unique in the account and Region. When private access is on, AWS publishes a record for each hostname that points at the private IPs of the endpoint it lists. The docs tell you to verify this with nslookup "from any network", so these are AWS-managed records, not a private hosted zone you create.
- 1The engineer's machine looks up a hostname, such as payments-111122223333.eks-capabilities.us-east-1.amazonaws.com. AWS-managed DNS returns the shared endpoint's private IPs. The platform hostname returns the same IPs.
- 2The browser opens a TLS connection to one of those IPs and sends the hostname it looked up in the TLS handshake (SNI) and the Host header.
- 3A request for the payments hostname reaches the payments Argo CD.
- 4A request for the platform hostname, through the same IPs, reaches the platform Argo CD.
Two caveats. First, the AWS documentation does not state outright that two capabilities can list the same endpoint, or that routing is by hostname. Steps 2 to 4 are how other shared AWS interface endpoints behave, such as private API Gateway, and what the per-instance hostnames imply. Confirm it with two capabilities before you depend on it. Second, the documented multi-endpoint case is the reverse: one capability listing several endpoints, "to enable access from multiple VPCs". Each hostname has one public record, and the docs do not say which endpoint's IPs it returns when it lists endpoints in two VPCs. One central endpoint in a network VPC that every other network routes to sidesteps both questions: one set of IPs, one security group, and any number of Argo CD instances behind it.
No load balancer, Ingress, ACM certificate or Route 53 private hosted zone appears anywhere in this design. If a design review asks where the ALB goes, the answer is that there isn't one. Those pieces only exist for a self-managed Argo CD running in the cluster.
4Identity: Identity Center is the only way in
The capability authenticates people only through IAM Identity Center. The awsIdc block (the Identity Center instance ARN, plus an optional Region if the instance lives in a different Region from the cluster) is required when you create the capability. Local Argo CD users, Dex and custom OIDC or SAML providers are not supported. Corporate identity providers such as Okta, Entra ID or ADFS work only by federating into Identity Center, which most AWS organizations already do.
Authorization has two levels, and both have to say yes:
- 1The user authenticates at the corporate identity provider, which federates into IAM Identity Center.
- 2Identity Center passes the user's ID and group IDs to Argo CD, which looks them up in the capability's rbacRoleMappings.
- 3A match grants one global role: ADMIN, EDITOR or VIEWER. No match means no access, whatever else is configured.
- 4AppProject roles, keyed on Identity Center group IDs, decide which Applications the user can see, sync or delete.
- Global roles are fixed. ADMIN can do everything, including registering clusters and repositories. EDITOR can update existing projects and their roles but cannot change global settings, clusters or repositories. VIEWER is read-only. Role names are case-sensitive uppercase.
- EDITOR and VIEWER see no Applications by default. They need a project role. ADMIN sees everything without one.
- Mappings use IDs, not names. Both rbacRoleMappings and AppProject groups take Identity Center user and group IDs.
- There is a ceiling. One capability supports up to 1,000 identities, counting users and groups. Map groups, not people.
The pattern the AWS docs recommend for teams: the platform team maps to ADMIN, team leads to EDITOR, and all developers to VIEWER. ADMIN creates one AppProject per team, and each team lead grants their developers project roles inside it.
aws eks update-capability \
--region us-east-1 \
--cluster-name platform-hub \
--capability-name argocd \
--configuration '{
"argoCd": {
"rbacRoleMappings": {
"addOrUpdateRoleMappings": [
{ "role": "ADMIN",
"identities": [{ "id": "<platform-team-group-id>", "type": "SSO_GROUP" }] },
{ "role": "EDITOR",
"identities": [{ "id": "<team-leads-group-id>", "type": "SSO_GROUP" }] },
{ "role": "VIEWER",
"identities": [{ "id": "<all-developers-group-id>", "type": "SSO_GROUP" }] }
]
}
}
}'apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: payments
namespace: argocd
spec:
sourceNamespaces: # required by the managed capability
- argocd
sourceRepos:
- https://github.com/acme/payments-deploy
destinations:
- server: arn:aws:eks:us-east-1:222233334444:cluster/prod-a
namespace: 'payments-*'
roles:
- name: developer
policies:
- p, proj:payments:developer, applications, *, payments/*, allow
- p, proj:payments:developer, clusters, get, *, allow # show cluster names in the UI
groups:
- <payments-devs-group-id> # Identity Center group IDThe identity type is SSO_GROUP for groups and SSO_USER for individual users. One more point from the docs: Argo CD permissions are not Kubernetes permissions. Anyone who can create an Application can deploy to any cluster Argo CD can reach, within their project's limits. Keep each project's destinations narrow.
5The CLI and CI/CD without argocd login
The Argo CD CLI works against the managed endpoint, but not through SSO. argocd login is not supported, and neither are argocd admin commands, which need direct pod access. You set three environment variables instead:
export ARGOCD_SERVER=$(aws eks describe-capability \
--region us-east-1 --cluster-name platform-hub --capability-name argocd \
--query 'capability.configuration.argoCd.serverUrl' --output text | sed 's|^https://||')
export ARGOCD_AUTH_TOKEN="<token generated in the Argo CD UI>"
export ARGOCD_OPTS="--grpc-web"
argocd app list
argocd app sync payments/checkout-api
- Admin account tokens are for setup only. Generate one under Settings, Accounts, admin. Only 5 can exist at a time, and AWS recommends an expiry of 12 hours at most.
- Pipelines use project-role tokens. A token scoped to one AppProject role can sync that project's Applications and nothing else. Store it in your CI system's secret store and rotate it.
- The pipeline needs the same network path as people. With private access on, a hosted runner on the internet cannot reach the endpoint. Run deployment jobs on runners inside the connected network, or let Git drive the sync and keep the pipeline out of Argo CD entirely.
- AWS-side calls are separate. describe-capability, update-capability and access entry calls are ordinary AWS API calls, controlled by IAM.
6Hub-and-spoke across accounts and Regions
The fleet design is one capability on a hub cluster in a platform account, deploying to workload clusters in other accounts. The capability does not set up this topology, or even the local cluster, for you. You register every target, including the hub itself, and grant access on each one.
- 1Argo CD pulls the fleet manifests from Git.
- 2It reads the cluster Secrets in the hub's argocd namespace. Each one names a target by its EKS cluster ARN, and ApplicationSets generate one Application per cluster from them.
- 3It acts as the capability's IAM role in the platform account.
- 4It calls the prod-a Kubernetes API in account A. The Access Entry for that role, with its access policy, decides what it may change.
- 5It does the same for prod-b in account B, in another Region, through the same process.
Compared with a self-managed hub, three parts of the usual design disappear:
- No IRSA or role chaining. You do not create roles in the spoke accounts or write cross-account trust policies. The Access Entry on the target cluster names the hub's capability role directly.
- No network path. AWS manages connectivity from the capability to target clusters, including fully private ones. You don't need VPC peering, Transit Gateway routes or a public API endpoint on the spokes.
- Regions are not special. The cluster ARN includes the Region, so a cross-Region target is registered the same way as a local one.
# Run in the workload account, against the target cluster.
aws eks create-access-entry \
--region us-east-1 \
--cluster-name prod-a \
--principal-arn arn:aws:iam::111122223333:role/ArgoCDCapabilityRole \
--type STANDARD
# Write access only where this cluster's apps live.
aws eks associate-access-policy \
--region us-east-1 \
--cluster-name prod-a \
--principal-arn arn:aws:iam::111122223333:role/ArgoCDCapabilityRole \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy \
--access-scope type=namespace,namespaces=payments-prod# Argo CD must READ every resource type cluster-wide for health checks
# and drift detection, even if it only WRITES to a few namespaces.
aws eks associate-access-policy \
--region us-east-1 \
--cluster-name prod-a \
--principal-arn arn:aws:iam::111122223333:role/ArgoCDCapabilityRole \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminViewPolicy \
--access-scope type=cluster# Committed to Git, or applied to the hub cluster.
apiVersion: v1
kind: Secret
metadata:
name: prod-a
namespace: argocd
labels:
argocd.argoproj.io/secret-type: cluster
env: prod # ApplicationSet cluster generators select on labels
stringData:
name: prod-a
server: arn:aws:eks:us-east-1:222233334444:cluster/prod-a # the ARN, never the API URL
project: payments- The hub needs registering too. EKS creates an Access Entry for the capability role on the hub cluster, but with no Kubernetes permissions. If the hub only manages other clusters, leave it that way. If it also runs workloads, register it by ARN (the docs suggest the name in-cluster for compatibility with upstream examples) and grant it permissions like any spoke.
- Do not ship AmazonEKSClusterAdminPolicy. The docs use it to get started quickly and warn against it in production, because it is equivalent to system:masters. Use cluster-wide read access plus namespace-scoped write access.
- Grant reads with an access policy, not a ClusterRole. The AWS docs show a ClusterRole bound to the group eks-access-entry:<role-ARN>. On a test build of this design, that binding never applied: Argo CD failed with apiservices.apiregistration.k8s.io is forbidden and the Application stayed Unknown. Associating AmazonEKSAdminViewPolicy at cluster scope fixed it within a minute. It also keeps every permission in one place, the Access Entry.
- Only EKS clusters can be targets. An on-premises or other-cloud cluster has no EKS ARN to register.
- Registration status starts at Unknown. A new cluster shows an Unknown connection state until an Application targets it. That does not mean registration failed.
7Managed or self-managed
Almost every access decision above is simpler than its self-managed equivalent. The price is a list of things you cannot change.
| Concern | EKS Capability for Argo CD | Self-managed (Helm on EKS) |
|---|---|---|
| How people reach the UI | AWS-hosted endpoint; private through an eks-capabilities VPC endpoint | You build it: Ingress, ALB or NLB, certificate, DNS |
| Network filter | Endpoint security group only (no endpoint policies) | Security groups, WAF, internal load balancer, anything you put in front |
| Sign-in | IAM Identity Center only; other IdPs through federation | Dex, any OIDC provider, local users |
| Authorization | Three fixed global roles, AppProject roles, 1,000 identities | Full argocd-rbac-cm policy language |
| CLI | Tokens over gRPC-web; no argocd login or argocd admin | Everything, including SSO login |
| Reaching spoke clusters | EKS Access Entries; AWS handles private connectivity | IRSA or Pod Identity, cross-account roles, a network path to each API server |
| Targets | EKS clusters, by ARN | Any conformant Kubernetes cluster |
| Extensibility | No Config Management Plugins, notifications controller or UI extensions; sync timeout fixed at 120 seconds | All upstream features |
| Operations | AWS installs, upgrades and scales it | You patch, scale, shard and keep it highly available |
Choose the managed capability when every target is EKS, Identity Center is already how people get into AWS, and nobody depends on notifications, plugins or custom RBAC rules. Stay self-managed when you need non-EKS targets, a sign-in path that cannot go through Identity Center, Argo CD notifications, Config Management Plugins, or fine-grained global policies. Moving across is supported. AWS documents a migration path: scale the old controllers to zero, export Applications, ApplicationSets and AppProjects, and rewrite destination.server to cluster ARNs.
8What the documentation does not answer yet
The capability launched in late 2025, and some questions an enterprise review will ask are not covered in the AWS documentation used here. Test these yourself before you commit:
- Whether the EKS console's Open Argo CD UI link still works from a browser outside the connected network once public access is blocked. Based on the DNS behavior, expect it not to.
- How Git webhooks from a SaaS Git host reach a private-only endpoint. The webhook source is on the internet, so plan on polling or a relay inside your network.
- Where actions inside Argo CD (syncs, sign-ins, RBAC decisions) are audited, and whether they reach CloudTrail.
- Which private IPs a hostname resolves to when one capability lists endpoints in two different VPCs.
- Per-capability limits on registered clusters or Applications beyond the identity and token limits, and the pricing at fleet scale.
Conclusion
Access to a managed Argo CD is a short list of decisions, none of which involve Kubernetes networking. Decide whether the endpoint stays public or goes behind an eks-capabilities VPC endpoint in a network VPC that your hybrid links already reach. Decide which Identity Center groups get ADMIN, EDITOR and VIEWER, and give each team an AppProject with narrow destinations. Give pipelines project tokens and a runner that can reach the endpoint. Then grant one Access Entry per target cluster, with read access everywhere and write access only where that team deploys. The load balancers, certificates, Dex configuration and cross-account trust chains that a self-managed hub needs are simply gone. What remains is what you cannot customize, and that list decides whether the managed capability fits.
References
- Amazon EKS User Guide: Continuous deployment with Argo CD, Argo CD considerations, Comparing EKS Capability for Argo CD to self-managed Argo CD.
- Amazon EKS User Guide: Create an Argo CD capability using the AWS CLI, Configure private endpoint access for Argo CD.
- Amazon EKS User Guide: Configure Argo CD permissions, Register target clusters.
- Amazon EKS API Reference: ArgoCdConfigRequest and ArgoCdConfigResponse.
- AWS Containers Blog: Deep dive: streamlining GitOps with Amazon EKS Capability for Argo CD. AWS News Blog: Announcing Amazon EKS Capabilities.