|
| 1 | +--- |
| 2 | +title: "Setting up PrivateLink in the AWS Console" |
| 3 | +sidebarTitle: "AWS Console setup" |
| 4 | +description: "Step-by-step guide for exposing a resource from your AWS account to Trigger.dev via PrivateLink." |
| 5 | +--- |
| 6 | + |
| 7 | +This guide walks through setting up the AWS side of a private connection: a Network Load Balancer (NLB), a target group pointing at the resource you want to expose, and a VPC Endpoint Service that authorizes Trigger.dev to consume it. |
| 8 | + |
| 9 | +<Info> |
| 10 | + Prefer Terraform? Open the "Add connection" page in the Trigger.dev dashboard and use the |
| 11 | + Terraform wizard to generate a ready-to-apply script. The wizard creates everything described |
| 12 | + below and pre-fills our AWS account ID for you. |
| 13 | +</Info> |
| 14 | + |
| 15 | +## Prerequisites |
| 16 | + |
| 17 | +Before you start you'll need: |
| 18 | + |
| 19 | +- An **AWS account** with permission to create VPC, EC2, and ELB resources |
| 20 | +- A **resource** in a VPC subnet that you want to expose (RDS instance, ElastiCache cluster, internal API, etc.) |
| 21 | +- The **Trigger.dev AWS account ID** — find this on the "Add connection" page in your Trigger.dev dashboard, in the "I have my details" or "Step-by-step guide" cards |
| 22 | +- A **VPC** that contains the resource, with at least one private subnet per Availability Zone you want to serve from |
| 23 | + |
| 24 | +<Note> |
| 25 | + PrivateLink connections are zonal. If your resource lives in a single AZ, your connection will |
| 26 | + only be available from that AZ. For higher availability, ensure target groups can route to |
| 27 | + multiple AZs. |
| 28 | +</Note> |
| 29 | + |
| 30 | +## Step 1: Create an internal Network Load Balancer |
| 31 | + |
| 32 | +The NLB is what PrivateLink exposes to Trigger.dev. It must be **internal** (not internet-facing). |
| 33 | + |
| 34 | +<Steps> |
| 35 | + <Step title="Open the EC2 console"> |
| 36 | + Go to **EC2 → Load Balancers → Create load balancer** and choose **Network Load Balancer**. |
| 37 | + </Step> |
| 38 | + <Step title="Configure the basics"> |
| 39 | + - **Name**: something descriptive, e.g. `trigger-postgres-nlb` |
| 40 | + - **Scheme**: **Internal** |
| 41 | + - **IP address type**: IPv4 |
| 42 | + </Step> |
| 43 | + <Step title="Choose VPC and subnets"> |
| 44 | + Pick the VPC where your resource lives. Select one private subnet per AZ that should serve traffic. |
| 45 | + Each subnet you select adds an availability zone to the endpoint. |
| 46 | + </Step> |
| 47 | + <Step title="Skip the listener for now"> |
| 48 | + You'll add a listener after creating the target group. You can leave the default placeholder |
| 49 | + listener and update it later, or remove it. |
| 50 | + </Step> |
| 51 | + <Step title="Create the load balancer"> |
| 52 | + Click **Create load balancer**. Provisioning takes 1–2 minutes. |
| 53 | + </Step> |
| 54 | +</Steps> |
| 55 | + |
| 56 | +## Step 2: Create a target group pointing at your resource |
| 57 | + |
| 58 | +The target group is how the NLB knows where to forward traffic. |
| 59 | + |
| 60 | +<Steps> |
| 61 | + <Step title="Open the target groups page"> |
| 62 | + Go to **EC2 → Target Groups → Create target group**. |
| 63 | + </Step> |
| 64 | + <Step title="Choose a target type"> |
| 65 | + - **IP addresses** for RDS, ElastiCache, or any resource you can reach by IP |
| 66 | + - **Instances** for EC2 instances you own |
| 67 | + - **Application Load Balancer** if your resource sits behind an ALB |
| 68 | + - **Lambda** for Lambda-backed services |
| 69 | + |
| 70 | + For most database use cases, **IP addresses** is correct. |
| 71 | + |
| 72 | + </Step> |
| 73 | + <Step title="Configure the target group"> |
| 74 | + - **Name**: e.g. `trigger-postgres-tg` |
| 75 | + - **Protocol**: TCP |
| 76 | + - **Port**: the port your resource listens on (5432 for Postgres, 6379 for Redis, 3306 for MySQL, etc.) |
| 77 | + - **VPC**: same VPC as the NLB |
| 78 | + - **Health check protocol**: TCP |
| 79 | + </Step> |
| 80 | + <Step title="Register your targets"> |
| 81 | + Add the IP addresses of the resource. For RDS, look up the writer endpoint's IPs (`dig <endpoint>` from inside the VPC). |
| 82 | + For ElastiCache, use the primary endpoint IPs. |
| 83 | + |
| 84 | + <Warning> |
| 85 | + RDS and ElastiCache endpoints' IP addresses can change after failover or maintenance. For long-lived |
| 86 | + connections, consider running a small Lambda or sidecar that periodically resolves the DNS name and |
| 87 | + updates the target group, or use a [DNS-resolved](https://aws.amazon.com/blogs/networking-and-content-delivery/hostname-as-target-for-network-load-balancers/) |
| 88 | + target if your setup supports it. |
| 89 | + </Warning> |
| 90 | + |
| 91 | + </Step> |
| 92 | + <Step title="Create the target group"> |
| 93 | + Click **Create target group**. |
| 94 | + </Step> |
| 95 | +</Steps> |
| 96 | + |
| 97 | +## Step 3: Add a listener on the NLB |
| 98 | + |
| 99 | +<Steps> |
| 100 | + <Step title="Open the NLB you created"> |
| 101 | + Go to **EC2 → Load Balancers**, select your NLB, and switch to the **Listeners** tab. |
| 102 | + </Step> |
| 103 | + <Step title="Add a TCP listener"> |
| 104 | + - **Protocol**: TCP |
| 105 | + - **Port**: same as your target group port (5432, 6379, etc.) |
| 106 | + - **Default action**: forward to the target group you just created |
| 107 | + </Step> |
| 108 | + <Step title="Save"> |
| 109 | + Click **Add**. The listener becomes active immediately. |
| 110 | + </Step> |
| 111 | +</Steps> |
| 112 | + |
| 113 | +<Tip> |
| 114 | + Test connectivity from a bastion host or another instance in the same VPC before continuing — |
| 115 | + e.g. `psql -h <nlb-dns-name> -p 5432 -U user -d db`. If the NLB can't reach your resource, the |
| 116 | + PrivateLink connection won't either. |
| 117 | +</Tip> |
| 118 | + |
| 119 | +## Step 4: Create a VPC Endpoint Service |
| 120 | + |
| 121 | +This is the resource that PrivateLink consumers connect to. |
| 122 | + |
| 123 | +<Steps> |
| 124 | + <Step title="Open the VPC console"> |
| 125 | + Go to **VPC → Endpoint services → Create endpoint service**. |
| 126 | + </Step> |
| 127 | + <Step title="Configure the endpoint service"> |
| 128 | + - **Name**: optional, but useful for identification, e.g. `trigger-postgres-endpoint` |
| 129 | + - **Load balancer type**: Network |
| 130 | + - **Available load balancers**: select the NLB you created |
| 131 | + - **Require acceptance for endpoint**: **No** (recommended) |
| 132 | + |
| 133 | + <Note> |
| 134 | + If you set "Require acceptance" to **Yes**, every connection request from Trigger.dev will |
| 135 | + sit in a pending state until you manually approve it. Setting it to **No** lets connections |
| 136 | + come up automatically once the principal is allow-listed. |
| 137 | + </Note> |
| 138 | + |
| 139 | + </Step> |
| 140 | + <Step title="Skip private DNS"> |
| 141 | + Leave the "Private DNS name" option disabled. Trigger.dev tasks dial the endpoint by IP via |
| 142 | + injected environment variables, so private DNS isn't needed. |
| 143 | + </Step> |
| 144 | + <Step title="Create the endpoint service"> |
| 145 | + Click **Create**. Note the **Service name** — it looks like `com.amazonaws.vpce.us-east-1.vpce-svc-0123abcd...`. |
| 146 | + You'll paste this into the Trigger.dev dashboard. |
| 147 | + </Step> |
| 148 | +</Steps> |
| 149 | + |
| 150 | +## Step 5: Authorize the Trigger.dev AWS account |
| 151 | + |
| 152 | +By default, no one can connect to your endpoint service. You need to explicitly allow Trigger.dev's AWS account. |
| 153 | + |
| 154 | +<Steps> |
| 155 | + <Step title="Open your endpoint service"> |
| 156 | + Go to **VPC → Endpoint services**, select the service you just created. |
| 157 | + </Step> |
| 158 | + <Step title="Open the Allow principals tab"> |
| 159 | + Click the **Allow principals** tab, then **Allow principals**. |
| 160 | + </Step> |
| 161 | + <Step title="Add Trigger.dev's account"> |
| 162 | + Paste the principal ARN in this format, replacing `<account-id>` with the Trigger.dev AWS |
| 163 | + account ID shown in your dashboard: |
| 164 | + |
| 165 | + ```text |
| 166 | + arn:aws:iam::<account-id>:root |
| 167 | + ``` |
| 168 | + |
| 169 | + <Warning> |
| 170 | + You will find the correct AWS account ID in the **Add connection** page of the Trigger.dev |
| 171 | + dashboard. Do not assume an account ID — it differs between Trigger.dev environments. |
| 172 | + </Warning> |
| 173 | + |
| 174 | + </Step> |
| 175 | + <Step title="Click Allow principals"> |
| 176 | + The principal is now authorized to create a VPC Endpoint targeting your service. |
| 177 | + </Step> |
| 178 | +</Steps> |
| 179 | + |
| 180 | +## Step 6: Add the connection in Trigger.dev |
| 181 | + |
| 182 | +<Steps> |
| 183 | + <Step title="Open the dashboard"> |
| 184 | + In Trigger.dev, go to **Organization Settings → Private Connections** and click **Add |
| 185 | + connection**. |
| 186 | + </Step> |
| 187 | + <Step title="Pick the I have my details card"> |
| 188 | + Then fill in: |
| 189 | + |
| 190 | + - **Friendly name**: a short identifier — this becomes the env var name. For `my-postgres` you'll get `TRIGGER_PRIVATE_LINK_MY_POSTGRES`. |
| 191 | + - **VPC Endpoint Service name**: paste the `com.amazonaws.vpce.<region>.vpce-svc-...` value from Step 4. |
| 192 | + - **Target region**: the AWS region your endpoint service lives in. |
| 193 | + |
| 194 | + </Step> |
| 195 | + <Step title="Submit"> |
| 196 | + Submit the form. The connection's status moves through **Pending → Provisioning → Active**. |
| 197 | + Provisioning typically takes 30–90 seconds. |
| 198 | + </Step> |
| 199 | + <Step title="Verify"> |
| 200 | + Once **Active**, the dashboard shows the assigned IPs. These are now available in your tasks |
| 201 | + via `process.env.TRIGGER_PRIVATE_LINK_<NAME>`. |
| 202 | + </Step> |
| 203 | +</Steps> |
| 204 | + |
| 205 | +## Troubleshooting |
| 206 | + |
| 207 | +<Expandable title="Status stays at Pending or Provisioning for several minutes"> |
| 208 | + - Confirm Trigger.dev's AWS account ID is in your endpoint service's **Allow principals** list. |
| 209 | + - Confirm the endpoint service is **Available** in the AWS console. |
| 210 | + - Confirm "Require acceptance" is set to **No** on the endpoint service. If it's set to **Yes**, |
| 211 | + the request is sitting in your pending queue and you must approve it manually. |
| 212 | +</Expandable> |
| 213 | + |
| 214 | +<Expandable title="Status is Active but my task can't connect"> |
| 215 | + - Confirm the NLB has a target registered and the target's health check is passing. |
| 216 | + - Confirm the listener port matches the port your task code is dialing. |
| 217 | + - Confirm the security group on your resource allows inbound traffic from the NLB or the VPC's |
| 218 | + private IP range. |
| 219 | + - Try connecting from inside the VPC first (e.g., a bastion host) to rule out resource-side |
| 220 | + issues. |
| 221 | +</Expandable> |
| 222 | + |
| 223 | +<Expandable title="Connection works but is slow"> |
| 224 | + - Cross-region connections add ~10–30ms RTT depending on the regions involved. If your tasks run |
| 225 | + in a different region than your resource, expect higher latency. |
| 226 | + - The NLB and target group's health checks influence connection setup time. Tighter health check |
| 227 | + intervals reduce failover time after a backend goes unhealthy. |
| 228 | +</Expandable> |
| 229 | + |
| 230 | +<Expandable title="I want to remove a connection"> |
| 231 | + Delete the connection from **Organization Settings → Private Connections** in the Trigger.dev |
| 232 | + dashboard. We'll tear down our VPC Endpoint and remove the network policy automatically. You can |
| 233 | + then delete your VPC Endpoint Service, NLB, and target group on the AWS side. |
| 234 | +</Expandable> |
0 commit comments