> ## Documentation Index
> Fetch the complete documentation index at: https://docs.replit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up Tunnels

> Set up Replit Tunnels to connect projects to private TCP services, deploy a connector with Docker or Helm, verify endpoints, and manage enrollment keys.

## What this enables

> [!NOTE]
>
> Tunnels must be enabled for your workspace. If Tunnels is unavailable in your workspace settings, contact Replit to confirm availability and enablement.

A tunnel lets a supported Replit Development Environment reach an approved TCP destination in your private network through a connector you operate. This is an alternative connectivity approach to allowing sandbox egress IPs into your network.

Run the connector in a network where it can resolve and reach your private TCP services. You can use the generated Docker command or deploy the connector on Kubernetes with the Helm chart below. Replit operates the tunnel control plane. The chart below deploys only your connector.

Connection path: Replit project → Replit tunnel infrastructure → your connector → private TCP service. Service authentication and TLS remain independent of tunnel enrollment.

## 1. Create the tunnel in Replit

1. In Replit, use the workspace selector to choose the workspace that owns your projects. Open Settings → Workspace overview and scroll to Tunnels.

![Open the Tunnels section in your workspace settings.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/tunnels-create.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=074526eae05d0445b5277ce23d8d62f5)

Open the Tunnels section in your workspace settings.

2. Click Create tunnel and enter a descriptive name.

![Create a tunnel.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/create-dialog.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=b3d3ae820d6cf213aef88dfa12be42c7)

Create a tunnel.

3. In the Save your enrollment key dialog, use the copy buttons beside Tunnel ID and Enrollment key. Save the enrollment key in your approved secrets manager before clicking I’ve saved my key. The key is shown only once and cannot be viewed again after closing this dialog.

![The one-time enrollment-key dialog. The key is intentionally hidden in this screenshot.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/save-key.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=37e69ca9e1702744b64a7c7de8530ca6)

The one-time enrollment-key dialog. The key is intentionally hidden in this screenshot.

4. Open Settings → Workspace overview → Tunnels and select your tunnel. Its ID appears beneath the tunnel name with a copy button. Before the connector is online, the Complete tunnel setup panel shows the Docker command. Replace the enrollment-key placeholder with the key you saved.

![Copy the Docker command from Complete tunnel setup.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/setup.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=bf1e6b498d5f788a606142f93e2f21a9)

Copy the Docker command from Complete tunnel setup.

For Helm, copy the full `RPTUN_TUNNEL_REF` and `RPTUN_ENROLL_ADDR` values from the setup command into `tunnelRef` and `enrollAddr`. Copy the container image from the command’s final line into `image`. The full tunnel reference is different from the short tunnel ID shown beneath the tunnel name. The enrollment key comes from the one-time key dialog, not from the setup command.

Run the Docker command on a host that can resolve and reach your private service. For Kubernetes, use the Helm chart below.

## 2. Deploy the connector with Helm

This example chart runs the customer-side connector on Kubernetes. Adapt it to your cluster's standards and validate it in a non-production environment before you roll it out.

### Create the chart files

Create this directory structure:

```txt theme={null}
replit-tunnel-connector/
  Chart.yaml
  values.yaml
  templates/
    deployment.yaml
customer-values.yaml
```

```bash theme={null}
mkdir -p replit-tunnel-connector/templates
```

`replit-tunnel-connector/Chart.yaml`

```yaml theme={null}
apiVersion: v2
name: replit-tunnel-connector
description: Example customer-side Replit tunnel connector.
type: application
version: 0.1.0
```

`replit-tunnel-connector/values.yaml`

```yaml theme={null}
# Use the image and connection settings from your own tunnel's setup panel.
# Do not put enrollment keys in Helm values or --set arguments.
image: ""
tunnelRef: ""
enrollAddr: ""
existingSecret: replit-tunnel-enrollment
secretKey: enrollment-key
resources: {}
nodeSelector: {}
tolerations: []
affinity: {}
```

`replit-tunnel-connector/templates/deployment.yaml`

```yaml theme={null}
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ .Release.Name }}
  labels:
    app.kubernetes.io/name: replit-tunnel-connector
    app.kubernetes.io/instance: {{ .Release.Name }}
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app.kubernetes.io/name: replit-tunnel-connector
      app.kubernetes.io/instance: {{ .Release.Name }}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: replit-tunnel-connector
        app.kubernetes.io/instance: {{ .Release.Name }}
    spec:
      automountServiceAccountToken: false
      containers:
        - name: connector
          image: {{ required "Set image to the connector image from your tunnel setup panel" .Values.image | quote }}
          imagePullPolicy: IfNotPresent
          env:
            - name: RPTUN_ENROLLMENT_KEY
              valueFrom:
                secretKeyRef:
                  name: {{ required "Set existingSecret" .Values.existingSecret | quote }}
                  key: {{ required "Set secretKey" .Values.secretKey | quote }}
            - name: RPTUN_TUNNEL_REF
              value: {{ required "Set tunnelRef from your tunnel setup panel" .Values.tunnelRef | quote }}
            - name: RPTUN_ENROLL_ADDR
              value: {{ required "Set enrollAddr from your tunnel setup panel" .Values.enrollAddr | quote }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      nodeSelector:
        {{- toYaml .Values.nodeSelector | nindent 8 }}
      tolerations:
        {{- toYaml .Values.tolerations | nindent 8 }}
      affinity:
        {{- toYaml .Values.affinity | nindent 8 }}
```

### Configure your tunnel

Create the namespace and provision the enrollment key through your approved secret-management process. The example expects a Secret named `replit-tunnel-enrollment` with a field named `enrollment-key` in namespace `replit-tunnels`. Do not put the enrollment key in Helm values, source control, or `--set` arguments.

`customer-values.yaml` — replace the three placeholders using your own tunnel’s generated setup command:

```yaml theme={null}
# Replace all three placeholders using your own tunnel's setup command.
image: "CONNECTOR_IMAGE_FROM_REPLIT"
tunnelRef: "TUNNEL_REFERENCE_FROM_REPLIT"
enrollAddr: "ENROLLMENT_ADDRESS_FROM_REPLIT"
existingSecret: replit-tunnel-enrollment
secretKey: enrollment-key
```

Use an approved immutable connector image digest for production where available. The chart preserves the image entry point and passes the environment variables shown in the generated Docker command. Validate compatibility with your exact image.

### Render and install

With Helm and `kubectl` configured for your intended Kubernetes cluster, run these commands from the directory containing `customer-values.yaml` and the chart directory. The namespace and Secret must already exist:

```bash theme={null}
helm lint ./replit-tunnel-connector -f customer-values.yaml
helm template replit-tunnel ./replit-tunnel-connector \
  --namespace replit-tunnels -f customer-values.yaml
helm upgrade --install replit-tunnel ./replit-tunnel-connector \
  --namespace replit-tunnels -f customer-values.yaml \
  --wait --timeout 5m
kubectl -n replit-tunnels rollout status deployment/replit-tunnel
kubectl -n replit-tunnels get pods -l app.kubernetes.io/instance=replit-tunnel
```

The chart runs one replica with the `Recreate` strategy, so updates cause downtime. It creates no public Service or LoadBalancer and defines no health probes. Kubernetes readiness alone does not prove tunnel connectivity: confirm **Online** in Replit and complete the project Shell tests below.

Before production use, validate resources, CPU architecture, writable-state needs, security context, restart behavior and outbound network requirements. The connector must reach your private service’s DNS hostname and TCP port. Obtain the complete connector egress requirements from Replit.

For troubleshooting, review logs locally and redact credentials and private information before sharing:

```bash theme={null}
kubectl -n replit-tunnels logs deployment/replit-tunnel --tail=100
```

## 3. Confirm the connector is online

Return to Settings → Workspace overview → Tunnels and select your tunnel. Wait for the Status card to show Online. If it stays offline, check enrollment configuration, outbound network access, pod events, and connector logs.

![Online means the connector is connected. Never is expected before any application traffic.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/online-empty.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=7930e84bdab9e71e80c25dee819bb7cd)

Online means the connector is connected. Never is expected before any application traffic.

## 4. Register a private endpoint

1. On the tunnel details page, click Add endpoint in the Endpoints section.
2. Enter a descriptive name and the private destination hostname with its TCP port, for example database.internal.example:5432.
3. Use a hostname resolvable from the connector's network. The current UI does not support IP-address destinations.
4. Select Test connection. A green check means the connector can reach the destination.
5. Add the endpoint and confirm it appears in the table.

![Successful connection test: the green check beside Destination confirms the connector can reach the service.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/endpoint-connection-test-success.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=05866622a7ec96e3fd06c80010bb9373)

Successful connection test: the green check beside Destination confirms the connector can reach the service.

The endpoint appears in the table, where you can copy its destination or open its three-dot actions menu.

![Saved Redis endpoint with the connector Online and destination Reachable.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/endpoint.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=b957c4f84856b532d427d558b723b7e2)

Saved Redis endpoint with the connector Online and destination Reachable.

## 5. Verify endpoint resolution in a Replit project Shell

Use the workspace selector to return to the workspace that owns the tunnel, open a project there, and open the **Shell** tab in the project editor. Run the following checks in that Shell, not in a standalone chat.

### Confirm the hostname resolves

Use the hostname from the endpoint’s Destination field without the port or a URL scheme:

```bash theme={null}
getent hosts YOUR_ENDPOINT_HOSTNAME
```

A successful lookup prints an IP address and the hostname. DNS resolution alone does not confirm service connectivity; run the service test below next.

### Test the service connection

Use your service’s native client for a lightweight request, with its normal TLS and authentication requirements. For Redis:

```bash theme={null}
redis-cli -h YOUR_ENDPOINT_HOSTNAME -p 6379 PING
```

`6379` is Redis’s default port. Replace it if needed to match the port of your registered endpoint.

Expected result: `PONG`. This is Redis’s application-level `PING`, not ICMP network ping.

![Hostname resolution and a successful Redis PONG in the project Shell.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/shell-proof.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=0cac629de7d995bb6424d2b0d84a1a22)

Hostname resolution and a successful Redis PONG in the project Shell.

After a successful request, the tunnel dashboard now shows recent traffic timestamps.

![Tunnel activity after the successful Redis request.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/traffic.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=089a5eed1d316701d3ac49647e7c15b5)

Tunnel activity after the successful Redis request.

## 6. Manage enrollment keys

Back in the tunnels dashboard, open the three-dot menu beside the tunnel name and select **Manage enrollment key**.

![Open enrollment-key management.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/key-menu.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=bb6c5735f27d191245ea425601f8f4fe)

Open enrollment-key management.

![Active key list and available actions.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/keys.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=bb4515355377b012c9b7f0d59a46a436)

Active key list and available actions.

### Add an enrollment key

Choose Add enrollment key when a slot is available. It opens the same Save your enrollment key dialog shown during initial setup. Copy the new key immediately and save it securely: it is shown only once.

If both key slots are occupied, identify the key to revoke or replace before continuing. Do not revoke a key blindly. Retry key delivery is for an interrupted request, not for displaying a previously delivered key again.

### Revoke an enrollment key

Choose **Revoke** on the intended key, check the selected key details, and choose **Confirm selected key revocation**. This blocks future enrollment using that key.

> [!WARNING]
>
> Revoking a key does not disconnect established connector sessions. Restart the connector with a new key to end existing sessions.

![Review and confirm revocation of the selected key.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/revoke.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=6d448ab1dc83729ef72f0678341b4360)

Review and confirm revocation of the selected key.

![After revocation, the active key count updates. The established connector can still be Online.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/revoked.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=02695158c64bb5c4155f35864888255f)

After revocation, the active key count updates. The established connector can still be Online.

### Update a connector after changing its key

Provision the new key through your approved secrets manager. Restart the connector deployment after the Secret update:

```bash theme={null}
kubectl -n replit-tunnels rollout restart deployment/replit-tunnel
kubectl -n replit-tunnels rollout status deployment/replit-tunnel
```

Confirm **Online** and repeat the project Shell service request. Plan for downtime with this example’s single-replica `Recreate` strategy. Do not assume that revocation alone terminates an existing connection.

## 7. Disconnect and clean up

When the connector stops, the tunnel becomes Offline and the endpoint should not be treated as reachable.

![Tunnel Offline and endpoint Unreachable after disconnecting the connector.](https://mintcdn.com/replit/tf-DypNXeTHeMPj_/images/tunnels/offline.png?fit=max\&auto=format\&n=tf-DypNXeTHeMPj_\&q=85\&s=6deb9a3547a92b22a887feb6179614da)

Tunnel Offline and endpoint Unreachable after disconnecting the connector.

To remove the connector’s Helm release:

```bash theme={null}
helm uninstall replit-tunnel --namespace replit-tunnels
```

The chart does not manage the Secret, namespace, Replit tunnel, or endpoints. Remove those separately only when they are no longer needed. Never remove a shared namespace or another application’s endpoint.

## Troubleshooting

### Tunnels unavailable

Ask Replit to verify workspace enablement and service availability before changing your connector or network configuration.

### Connector pod fails

Inspect image pull, scheduling, Secret references, and container startup errors.

### Tunnel offline

Inspect enrollment configuration and connector outbound connectivity.

### Endpoint unreachable

Check private DNS, the target service and port, routing, firewall rules, and any Kubernetes NetworkPolicies from the connector’s network.

### Connection test succeeds but query fails

Check database TLS, credentials, database permissions, and the supported Replit sandbox routing path.

### Duplicate destination

Check existing endpoints across the workspace before creating another registration.

### Hostname does not resolve

Confirm you are in a Replit project **Shell** in the same workspace as the saved endpoint. Check the exact destination hostname and tunnel status.

If it persists, contact Replit with the workspace/project and error. **Never share the enrollment key.**
