---
description: Giving private-network instances inbound and outbound internet access
revision_date: 20.10.2026
---

# Internet access

!!! warning "Draft — pilot feature"
    Private Networks is in a limited pilot and this page is a work in progress.

An instance with **only a private IP has no internet access in either
direction**: it cannot be reached from the internet, and it cannot reach the
internet.

There are three ways to change that:

## Option 1. Give the instance a public IP

The simplest option. Choose **Public + Private IP** at deploy time. The instance
gets a public IPv4 1:1-NAT'd to its private address, with both inbound and
outbound internet access. Verda does not filter inbound traffic, so run a
guest firewall such as `ufw` or `nftables` on the instance, see
[Securing your instance](https://docs.verda.com/cpu-and-gpu-instances/securing-your-instance/).

## Option 2. Jump host

Deploy one instance with a public IP and keep every other instance private. The
public one acts as a bastion: you SSH to it, and from there to the private
instances over their private addresses.

```bash
# One hop, using the jump host as a proxy
ssh -J root@<jump-host-public-ip> root@10.0.0.5
```

This gives you *access*, not egress, the private instances still have no
outbound internet.

## Option 3. Route the subnet's egress through a NAT instance

To give private instances outbound internet, add a **route** on the subnet that
sends `0.0.0.0/0` through one of the network's instances.

Requirements for the target instance:

- it must be on the same private network;
- it must have a **public IP**.

You are responsible for configuring forwarding and NAT on that instance. It is
an ordinary instance you own, billed as one, and it is a single point of failure
for the subnet's egress.

### Guest-side configuration

SSH into the instance you pointed the route at, and paste the following.

!!! warning "This script removes `ufw`"
    It installs `iptables-persistent`, which conflicts with `ufw` on Ubuntu. If `ufw status` reports `Status: active`, recreate your inbound rules with `iptables` after running the script and save them with `netfilter-persistent save`.

```bash
cat > setup-gateway <<'GWEOF'
#!/usr/bin/env bash

set -euo pipefail

IFACE=$(ip -o -4 route show to default | awk '{print $5; exit}')

cat > /etc/sysctl.d/99-nat-gateway.conf <<EOF
net.ipv4.ip_forward = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
# The kernel ORs the per-interface value with the ones above, so clear it too,
# or the gateway ICMP-redirects clients to a next hop that will not NAT them.
net.ipv4.conf.$IFACE.send_redirects = 0
EOF
sysctl --system >/dev/null

iptables -t nat -A POSTROUTING -o "$IFACE" -j MASQUERADE

# Docker sets the FORWARD policy to DROP and leaves DOCKER-USER for our rules.
iptables -nL DOCKER-USER >/dev/null 2>&1 \
    && iptables -I DOCKER-USER -i "$IFACE" -o "$IFACE" -j ACCEPT \
    || iptables -A FORWARD -i "$IFACE" -o "$IFACE" -j ACCEPT

apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq iptables-persistent
netfilter-persistent save

echo "NAT gateway ready on $IFACE"
GWEOF

chmod +x setup-gateway
./setup-gateway
```

Verify from a private instance:

```bash
curl -s https://ifconfig.me    # should print the gateway's public IP
```

## Managed internet gateway (coming soon)

The **Enable internet gateway** toggle in the subnet dialog is not yet
functional. When it's available, it will give the subnet shared outbound internet
through a Verda-managed gateway.
