# Telnyx Compute: Private Ingress — Full Documentation > Complete page content for Private Ingress (Compute section) of the Telnyx developer docs (https://developers.telnyx.com). > This file: https://developers.telnyx.com/docs/development/llms/compute-private-ingress-llms-full-txt · Root index: https://developers.telnyx.com/llms.txt ## Get Started ### Overview > Source: https://developers.telnyx.com/docs/edge-compute/private-ingress.md **Beta:** Private Ingress is in beta. Private Ingress runs an Edge Compute function for the user's own networks only. The function has no public URL. Devices connected to a [*Network*](/docs/network/overview) request it by name, and nothing on the public internet reaches it. ## What It Enables - Internal tools, admin APIs, and back ends run on Edge Compute without exposure to the internet. - Any [WireGuard peer](/docs/network/wireguard-interfaces) on the *Network* reaches the function. - Each function has a stable private name, such as `hello-private-16b97074-7.private.telnyxcompute.com`. - More functions join the same *Network* with no new network setup. ## Next Steps - [Quick Start](/docs/edge-compute/private-ingress/quick-start) deploys a private function and requests it by name from a laptop on a WireGuard VPN. - [Function Settings](/docs/edge-compute/private-ingress/how-it-works/settings) explains each setting. --- ### Quick Start > Source: https://developers.telnyx.com/docs/edge-compute/private-ingress/quick-start.md This guide connects a laptop to a [*Network*](/docs/network/overview) over WireGuard, deploys a private function on the same *Network*, and requests the function by its private name. ## Requirements - `telnyx-edge` v0.5.17 or later, authenticated with an API key - A laptop with WireGuard installed ## Create the *Network* ```bash telnyx-edge network create pi-demo ``` ```text Created network pi-demo (abd91c8d-579b-4f1f-9937-02165749de20) ``` ## Create the WireGuard Interface `telnyx-edge network regions --service cloud_vpn` lists the regions that offer WireGuard Interfaces. Create one in `ashburn-va` and wait until it is provisioned: ```bash telnyx-edge network interface create pi-demo --region ashburn-va --wait ``` ```text Created WireGuard Interface pi-demo-ashburn-va (3805266d-b535-419b-ac6e-baeb4199e074) in ashburn-va, status provisioning Waiting up to 30m0s for 3805266d-b535-419b-ac6e-baeb4199e074 to provision... status: provisioned Bind a function to it in telnyx.toml: [network.PI_DEMO] id = "3805266d-b535-419b-ac6e-baeb4199e074" ``` The printed `id` is the WireGuard Interface UUID the function binding needs. ## Add a DNS Resolver The DNS resolver answers the private names of functions on the *Network*. Create it in the same region and wait until it is provisioned: ```bash telnyx-edge network dns create pi-demo --region ashburn-va --wait ``` The command prints the resolver's address on the *Network*, `172.27.240.14`, once it is provisioned. ## Create the Function ```bash telnyx-edge new-func -l js -n internal-api cd internal-api ``` Add `private = true` to the `[edge_compute]` block of `func.toml`, and add the binding the interface command printed, with `peer = false` and `ingress = true`: ```toml [edge_compute] func_id = "5aca61c5-4010-4edc-8f88-554e06372194" func_name = "internal-api" private = true [network.PI_DEMO] id = "3805266d-b535-419b-ac6e-baeb4199e074" peer = false ingress = true ``` [Function Settings](/docs/edge-compute/private-ingress/how-it-works/settings) explains each setting. ## Ship ```bash telnyx-edge ship ``` ```text ... ✅ Func 'internal-api' is now deployed! (shipped in 5m51s) 🔒 Public access is off (private = true). 🔒 Private access (from your network): Address: 172.27.0.2 Host: internal-api-5aca61c5-4.private.telnyxcompute.com WireGuard peer: fddb82c9-b6d9-435a-82e8-b68fcd510827 (interface 3805266d-b535-419b-ac6e-baeb4199e074) Status: active ... ``` `Host` is the function's private name. `Address` is the ingress it sits behind. ## Connect the Laptop Add the laptop as a WireGuard peer. The command writes a ready-to-use configuration with the private key filled in: ```bash telnyx-edge network peer create pi-demo-ashburn-va laptop ``` ```text Created WireGuard peer laptop (9cf08232-b50c-4d20-a94a-755f1323e410) on interface 3805266d-b535-419b-ac6e-baeb4199e074 Wrote laptop.conf (mode 0600) Connect with: wg-quick up ./laptop.conf ``` ```ini [Interface] PrivateKey = ... Address = 172.27.0.3/32 DNS = 172.27.240.14 [Peer] PublicKey = LYqat7Pcivwc3AQfj6INfZ5PXT2n8KTLzIfnAY9/fnQ= AllowedIPs = 172.27.0.0/24, 172.27.240.14/32 Endpoint = 64.16.243.3:5167 PersistentKeepalive = 1 ``` `DNS` points the laptop at the resolver while the tunnel is up. `AllowedIPs` routes the interface subnet and the resolver through the tunnel. Bring up the tunnel: ```bash sudo wg-quick up ./laptop.conf ``` ## Request the Function by Name ```bash curl http://internal-api-5aca61c5-4.private.telnyxcompute.com/ ``` ```json {"message":"Hello from Telnyx Edge Compute!"} ``` The public URL returns `404`: ```bash curl https://internal-api-5aca61c5-4.telnyxcompute.com/ ``` ```text 404 page not found ``` To reach the function from every device on a Tailscale network instead of one WireGuard peer per device, add a Tailscale Subnet Router: see [Tailscale](/docs/edge-compute/private-ingress/how-it-works/tailscale). ## Clean Up ```bash telnyx-edge delete-func internal-api --yes telnyx-edge network peer delete laptop telnyx-edge network dns delete pi-demo-dns-ashburn-va telnyx-edge network interface delete pi-demo-ashburn-va telnyx-edge network delete pi-demo ``` --- ### Pricing > Source: https://developers.telnyx.com/docs/edge-compute/private-ingress/pricing.md Private Ingress is billed as a monthly recurring charge (MRC). | Resource | MRC | | --- | --- | | Ingress | $50 per ingress | | DNS resolver | $50 per DNS resolver | An account has one ingress per *Network*, shared by every function on that *Network* with `ingress = true`. Adding functions to an existing ingress adds no ingress charge. Functions, the [*Network*](/docs/network/overview), and its [WireGuard Interface](/docs/network/wireguard-interfaces) are billed at their own rates. See [Edge Compute pricing](/docs/edge-compute/platform/pricing). --- ## Reference ### Function Settings > Source: https://developers.telnyx.com/docs/edge-compute/private-ingress/how-it-works/settings.md Four settings in `func.toml` control how a function is reached. `private` sits in the `[edge_compute]` block. `id`, `peer`, and `ingress` sit in a [network binding](/docs/edge-compute/runtime/bindings/network) block. ```toml [edge_compute] func_id = "16b97074-7393-4a21-b95f-c8fec4a0138e" func_name = "hello-private" private = true [network.PRIVATE_NET] id = "241b79f1-c159-4fbd-b6da-41a77c731904" peer = false ingress = true ``` Shipping applies a change. ## `private` `private` decides whether the function has a public URL. | Value | Result | | --- | --- | | `false` (default) | The function answers at its [public URL](/docs/edge-compute/configuration/routing). | | `true` | The public URL returns `404`. The function answers only through the ingress. | ## `id` `id` is the UUID of a WireGuard Interface on the [*Network*](/docs/network/overview). It ties the function to that *Network*. The *Network* UUID is not accepted. ## `peer` and `ingress` `peer` and `ingress` pick which way traffic flows between the function and the *Network*. | Use case | Settings | Traffic | | --- | --- | --- | | Private Ingress | `peer = false`, `ingress = true` | Hosts on the *Network* send requests to the function. | | [Network binding](/docs/edge-compute/runtime/bindings/network) | `peer = true`, `ingress` omitted | The function sends requests to hosts on the *Network*. | - `ingress = true` puts the function behind the *Network*'s [ingress](/docs/edge-compute/private-ingress/how-it-works/ingress), which receives requests from the *Network* and hands them to the function. - `peer = true` adds the function to the WireGuard Interface as a peer of its own, so the function reaches other peers and hosts on the *Network*. It is the default. `telnyx-edge ship` rejects `peer = false` without `ingress = true`. ## Common Setups | Goal | `private` | `peer` | `ingress` | | --- | --- | --- | --- | | Internal service reached only from the *Network* | `true` | `false` | `true` | | Internal service that also reaches the *Network* | `true` | `true` | `true` | | Public function that also answers on the *Network* | `false` | `false` | `true` | | Public function that reaches the *Network* | `false` | `true` | `false` | --- ### Private Names > Source: https://developers.telnyx.com/docs/edge-compute/private-ingress/how-it-works/private-names.md Each function with `ingress = true` has a private name. ```text {func-name}-{func-id-prefix}.private.telnyxcompute.com ``` `func-id-prefix` is the first 10 characters of the function's ID, as in its [public URL](/docs/edge-compute/configuration/routing). ## DNS Resolver The DNS resolver on the [*Network*](/docs/network/overview) answers every private name with the address of the [ingress](/docs/edge-compute/private-ingress/how-it-works/ingress). It resolves other names as the public internet does. A WireGuard peer configuration names the resolver once the resolver is provisioned, so a peer requests a private function by name. Devices on a tailnet reach the resolver through a Tailscale Subnet Router and split DNS: see [Tailscale](/docs/edge-compute/private-ingress/how-it-works/tailscale). ```bash curl http://hello-private-16b97074-7.private.telnyxcompute.com/ ``` --- ### Tailscale > Source: https://developers.telnyx.com/docs/edge-compute/private-ingress/how-it-works/tailscale.md A [Tailscale Subnet Router](/docs/network/tailscale-subnet-router) on the [*Network*](/docs/network/overview) lets every device on your tailnet request private functions by their [private names](/docs/edge-compute/private-ingress/how-it-works/private-names), with no WireGuard peer per device. Set up the router as in the [Tailscale Subnet Router Quick Start](/docs/network/tailscale-subnet-router/quick-start), in the region of the *Network*'s DNS resolver. Private names then need two more things on the tailnet: the resolver's route and split DNS. | Tailnet setting | Value | | --- | --- | | Route | The WireGuard Interface subnet, such as `172.27.0.0/24`, which holds the [ingress](/docs/edge-compute/private-ingress/how-it-works/ingress) address | | Route | The DNS resolver's address as a `/32`, such as `172.27.240.14/32` | | Split DNS | `private.telnyxcompute.com` → the DNS resolver's address | ## The Resolver's Route The resolver adds its `/32` to the advertised routes of every router on the *Network*, whether the router or the resolver came first. You advertise only the interface subnet: ```bash telnyx-edge network router create pi-demo --region ashburn-va \ --route 172.27.0.0/24 --auth-key-file key.txt --wait ``` `telnyx-edge network router get pi-demo-ashburn-va-ts` then shows both routes under `Routes`. Approve the `/32` on the router's machine in the [Machines](https://login.tailscale.com/admin/machines) page of the admin console, as in Tailscale's [Enable subnet routes from the admin console](https://tailscale.com/docs/features/subnet-routers#enable-subnet-routes-from-the-admin-console), and grant access to it as for the interface subnet in the [Quick Start](/docs/network/tailscale-subnet-router/quick-start#prepare-the-tailnet). To cover it in advance, add the resolver's address to the `autoApprovers` routes and the access rule's `dst`. Resolvers currently get addresses in `172.27.240.0/21`. ## Split DNS Open the [DNS](https://login.tailscale.com/admin/dns) page of the Tailscale admin console and add a nameserver under **Nameservers**: - **Nameserver**: the resolver's address, `172.27.240.14` here. - **Restrict to domain**: on, with the domain `private.telnyxcompute.com`. Tailscale calls this a [restricted nameserver](https://tailscale.com/docs/reference/dns-in-tailscale#restricted-nameservers), or split DNS, and its guide to [route DNS lookups to an internal DNS server](https://tailscale.com/docs/features/subnet-routers#route-dns-lookups-to-an-internal-dns-server) shows the same setup. Devices keep their own DNS for every other name. Devices need to accept Tailscale's DNS settings as well as its [routes](/docs/network/tailscale-subnet-router/concepts/routes#for-the-tailnet-admin). macOS, Windows, iOS, and Android do both by default. Linux devices need [`tailscale set`](https://tailscale.com/docs/reference/tailscale-cli#set): ```bash tailscale set --accept-routes --accept-dns ``` ## Request the Function by Name ```bash curl http://internal-api-5aca61c5-4.private.telnyxcompute.com/ ``` The function sees the router's [`customer_address`](/docs/network/tailscale-subnet-router/security/source-address) as the sender, at the start of `X-Forwarded-For`. ## Several *Networks* in One Tailnet By default, Tailscale sends each route to one primary router, the one that joined the tailnet first, and moves it to another only when that router goes offline. Tailscale calls this [active-passive failover](https://tailscale.com/docs/features/route-selection#active-passive-failover). *Networks* often reuse addresses: a first WireGuard Interface is usually `172.27.0.0/24`, and resolvers in different regions can share an address. If routers of two *Networks* in one tailnet advertise the same route, requests reach whichever router is primary, which may be the other *Network*. [Route Approval](/docs/network/tailscale-subnet-router/security/route-approval) shows how to read which machine serves a route. Give each *Network* its own interface subnet with `telnyx-edge network interface create --subnet`. For a shared resolver address, turn the route off on the other router in its **Edit route settings**, in the [Machines](https://login.tailscale.com/admin/machines) page; removing its machine logs that router out for good. ## Troubleshooting | Symptom | Cause | | --- | --- | | The private name does not resolve | Split DNS is missing, the device does not accept Tailscale DNS, or the resolver's `/32` is not approved | | Requests time out or reach another service | Another router is primary for the route | | `Host is unreachable` on the first request | The ingress's WireGuard peer is still connecting; retry after a minute | | `404` with an empty body | The function's route is not ready yet, a few minutes after `ship`; retry | | `404 no private function is bound to this host` | The `Host` header is not a [private name](/docs/edge-compute/private-ingress/how-it-works/private-names) on this ingress | --- ### Ingress > Source: https://developers.telnyx.com/docs/edge-compute/private-ingress/how-it-works/ingress.md The ingress receives requests from the [*Network*](/docs/network/overview) and hands each one to a function. Each *Network* has one ingress, and every function on it with `ingress = true` sits behind that one address. - The ingress accepts HTTP on port 80. - The ingress is a peer of the WireGuard Interface named in the binding, called `ingress-`. - The first function with `ingress = true` on a *Network* creates the ingress, and deleting the last one removes it. Functions in between join and leave without interrupting the others. ## Selecting the Function The `Host` header names the function. It carries the function's [private name](/docs/edge-compute/private-ingress/how-it-works/private-names), or its public host name, which the ingress also accepts. A `Host` with no function behind it returns `404` with the body `no private function is bound to this host`. ## Forwarding Headers The function receives two forwarding headers. | Header | Value | | --- | --- | | `X-Forwarded-For` | Starts with the address of the host that sent the request, as the *Network* sees it | | `X-Forwarded-Host` | The name the request was sent to | --- ### Status > Source: https://developers.telnyx.com/docs/edge-compute/private-ingress/how-it-works/status.md `telnyx-edge inspect ` and `GET /v2/compute/funcs/{id}` report the ingress of a function with `ingress = true`. ```json "private_access": { "network_interface_id": "241b79f1-c159-4fbd-b6da-41a77c731904", "wireguard_peer_id": "de7c7281-4d06-4202-931f-e915a6793304", "address": "172.27.0.2", "hostnames": [ "hello-private-16b97074-7.private.telnyxcompute.com", "hello-private-16b97074-7.telnyxcompute.com" ], "status": "active" } ``` | Field | Description | | --- | --- | | `network_interface_id` | The WireGuard Interface named in the binding | | `wireguard_peer_id` | The ingress peer on that interface | | `address` | The ingress address | | `hostnames` | The names the ingress accepts for the function. The first is its private name. | | `status` | `provisioning` until the ingress routes to the function, then `active`. `error` means setup is failing and is being retried. | A function without `ingress = true` has no `private_access` field. ---