For the complete documentation index, see llms.txt. This page is also available as Markdown.

PrivateLink to Your Hub

Reach your Lunar Dedicated hub over a private AWS PrivateLink path from your own VPC, by creating one interface endpoint and two DNS records.

Your CI runners, CLI users, and browsers all need to reach your Lunar Dedicated install, and PrivateLink is the recommended way to let them. The hub API and the UI answer only inside the networks you connect to them, and nothing is published to the internet. The alternative, a public hostname behind TLS and token authentication, is there for teams whose CI runs on shared hosted runners and so cannot reach a private endpoint.

This page is the setup guide for that path. It's written for whoever runs your AWS networking. You can forward it on its own.

This page covers reaching the hub in. Lunar reaching out to your internal systems (a self-managed Git platform, an internal registry or API) is a separate, independent setup, covered in PrivateLink to Your Internal Systems. Many installs need both.

Do you need this?

Three questions to decide.

1. Does your CI run in a network you control? PrivateLink reaches your install from a VPC, so self-hosted runners in AWS are the natural fit. Shared hosted runners (github.com's, gitlab.com's) aren't in your VPC and can't use the endpoint, so an install that serves those still needs the internet route for whatever they call.

2. How will your Git platform deliver webhooks? Your Git platform calls the hub every time someone pushes, and it isn't on your runner network, so the endpoint you're about to create won't carry its traffic. This is the part most likely to have lead time, so settle it first: see Decide the webhook path.

3. How will developers reach the UI? The UI is Grafana, on your install's apex hostname, and a private install resolves only inside the VPCs you connect to it. If your developers already work behind a VPN, Direct Connect, or Transit Gateway that reaches the VPC, PrivateLink covers them once you add DNS forwarding and a route (step 5). If they don't, you would have to build that path first, and an install on the internet, where Grafana sits behind a login, may be the better trade.

How it works

Earthly publishes your install as an endpoint service. You create a matching interface endpoint in your VPC, plus two DNS records so your install's normal hostnames resolve to it.

Three properties are worth knowing up front, because they answer most security-review questions:

  • The connection is one-way. Your side initiates every session. Nothing in the install can initiate a connection back into your VPC.

  • Neither side learns the other's addresses. PrivateLink does not join the networks, add routes, or expose either CIDR range. Overlapping address ranges are fine.

  • You reach only the service Earthly publishes. Not the VPC it lives in, not anything else in Earthly's account.

You own and pay for the endpoint, the hosted zone, and any resolver infrastructure in step 5.

Checklist

If you've consumed endpoint services before, this is all you need. The Prefer Terraform? section has the resource definitions.

  1. Decide the webhook path. Most likely to have lead time, so start it first.

  2. Interface endpoint against Earthly's service name, in subnets whose zone IDs Earthly supports.

  3. Private DNS disabled on that endpoint. Leaving it on fails endpoint creation.

  4. Endpoint security group: inbound TCP 443 from your systems.

  5. Private hosted zone for your install's domain, associated with that VPC.

  6. Two alias A records in it, apex and wildcard, both targeting the endpoint's regional DNS name.

  7. Confirm it works and tell Earthly when you're good to go.

What you need from Earthly first

Earthly builds its side first, so you'll receive these before you start:

Value
Example

Region

us-east-2

Endpoint service name

com.amazonaws.vpce.us-east-2.vpce-svc-0123456789abcdef0

Supported availability zone IDs

use2-az1, use2-az2

Your install's domain

you.dedicated.earthly.dev

Hub hostname

hub.you.dedicated.earthly.dev

Port

TCP 443

Lunar Dedicated supports same-region endpoints only, so build everything here in the install's region.

Earthly also allowlists your AWS account beforehand, so your endpoint connects without an approval step. If your security team would rather Earthly allowlist one specific IAM role, send its ARN. It must be pathless, because AWS rejects principal ARNs containing a path on endpoint services.

Four names that look alike

Four names come up below. Three of them are vpce strings that are easy to mix up, and the fourth is what your CI actually calls.

Name
Looks like
Used for

Endpoint service name

com.amazonaws.vpce.us-east-2.vpce-svc-0123456789abcdef0

Earthly gives you this; it's the input when you create the endpoint

Endpoint ID

vpce-0123456789abcdef0

your endpoint, once created

Endpoint regional DNS name

vpce-0123456789abcdef0-a1b2c3d4.vpce-svc-0123456789abcdef0.us-east-2.vpce.amazonaws.com

the target your DNS records alias to

Your install's hostnames

you.dedicated.earthly.dev, hub.you.dedicated.earthly.dev

what your CI and browsers actually call

Decide the webhook path

Your Git platform delivers webhooks to the hub. Without them, pushes don't trigger anything and pull/merge-request status checks never post. Your Git platform isn't on your runner network, so the endpoint you're about to create won't carry that traffic.

Which case applies depends on where your platform runs:

  • A cloud-hosted Git platform (github.com, gitlab.com, etc.) delivers from the public internet and can't use PrivateLink. Earthly can leave just the webhook listener on the internet while everything else stays private. Webhooks are authenticated by a signed secret shared with the platform.

  • A self-managed Git platform in your own network can usually reach the hub through the same endpoint, or through a second one in whichever VPC it runs in. The steps are the same either way.

  • A managed single-tenant Git platform runs in your vendor's account. Some vendors offer an outbound private connection to a customer-published endpoint service; if yours does, request it and send Earthly the principal it gives you. This is usually a support ticket with a lead time you don't control, so raise it as soon as you have the endpoint service name. It runs in parallel with everything else here.

Step 1 — Create the interface endpoint

In your runner VPC: VPC → Endpoints → Create endpoint → Other endpoint services, paste Earthly's service name, verify it, then choose subnets.

Subnets must be in the zone IDs Earthly listed. AWS maps zone names like us-east-2a to different physical zones in different accounts, so a name means nothing across an account boundary:

Use two of the supported zones for redundancy; one is enough if that's all you have. If none of your subnets land in a supported zone, talk to Earthly before creating anything, because either side may need to add a subnet in a shared zone.

This constrains placement only: workloads anywhere in the VPC reach the endpoint across zones by ordinary VPC routing.

Attach a security group allowing inbound TCP 443 from your runners.

Step 2 — Turn private DNS off

Leave Enable DNS name unchecked (private_dns_enabled = false in Terraform).

Some tools default it on, and endpoint creation then fails with:

Enabling it asks AWS to resolve a hostname the provider has verified, and Earthly deliberately publishes none. Handling DNS on your side is what lets a single endpoint serve every hostname your install has today or gains later.

Step 3 — Create the private hosted zone

Create a private hosted zone named after your install's domain (you.dedicated.earthly.dev), associated with the VPC holding the endpoint. That zone is dedicated to this install, so shadowing all of it is safe.

Add two alias A records with the same target. You need both, because a wildcard doesn't match its own apex:

Record
Serves

you.dedicated.earthly.dev

the Grafana UI

*.you.dedicated.earthly.dev

hub. and everything else

The alias target is your endpoint's regional DNS name, the third row of Four names that look alike. Find it on the endpoint's Details tab under DNS names, or:

Use alias records rather than CNAMEs, because a CNAME isn't valid at a zone apex.

Step 4 — Point your CI at the hub

Nothing about Lunar's configuration changes for a private install:

The gRPC API and the HTTP endpoints share port 443, so only one port ever needs to be open. Authentication is the same bearer token you'd use over the internet.

Step 5 — Reach the install from elsewhere

Another VPC: repeat steps 1 to 3 in it. Each VPC gets its own endpoint and its own copy of the two records; the hostnames stay the same everywhere.

Laptops on the corporate network need two separate things, and it's easy to do only the first:

  1. DNS: forward you.dedicated.earthly.dev into the VPC, typically via a Route 53 Resolver inbound endpoint plus a conditional forwarder on your corporate resolvers.

  2. Network: a route from the corporate network to the endpoint's addresses (VPN, Direct Connect, or Transit Gateway), and the endpoint's security group must allow that source.

Resolver forwarding only makes the name resolve. Do the first without the second and the browser gets the right address with no way to reach it.

Prefer Terraform?

Steps 1 to 3 are five resources:

Confirm it works

From a host inside the VPC:

The first should return the endpoint's private addresses; public addresses mean your hosted zone isn't answering and traffic would be leaving your network. The second should return JSON.

An endpoint reaching available proves less than it sounds. It means the PrivateLink connection was established, not that DNS is right and not that anything behind it works. The two commands above are the real check.

Then tell Earthly three things, so the install can be verified end to end from both sides:

  • Your endpoint ID, so Earthly can confirm the connection is accepted and healthy rather than pending or rejected.

  • Which zone IDs you placed it in, which is what Earthly checks against if connectivity works from some subnets but not others.

  • Which webhook case applies, plus any principal to allowlist for a vendor-managed connection.

Troubleshooting

Endpoint creation fails on private DNS. Private DNS can't be enabled because the service ... does not provide a private DNS name means the flag is on. See step 2.

Endpoint creation fails on availability zones. Your subnets aren't in a zone the service supports. Compare zone IDs, not names.

Endpoint creation is rejected outright. Your account isn't allowlisted, or a different one is. An unlisted account can't see the service at all, so the failure never reaches Earthly's side. Confirm the account ID.

The endpoint sits in pendingAcceptance. Earthly's service accepts allowlisted connections automatically, so tell Earthly rather than waiting.

Hostnames don't resolve, or resolve to public addresses. The hosted zone isn't associated with the VPC you're testing from, or the records are missing. Test from inside that VPC; laptops need step 5.

Hostnames resolve but connections hang. Check the endpoint's security group allows TCP 443 from the client.

Only the apex works, or only hub. works. Only one of the two records exists, and both are needed.

Traffic works from one availability zone only. The alias targets a zonal endpoint DNS name rather than the regional one. See step 3.

A deeper hostname resolves but fails TLS. DNS wildcards match at any depth, so a.b.you.dedicated.earthly.dev resolves via your wildcard record, but the install's certificate only covers one level. Lunar only ever uses single-label names like hub., so this only appears if something is calling a name Lunar doesn't publish.

Everything works except webhooks. Your Git platform reaches the hub by a different path from your runners. See Decide the webhook path.

Last updated