---
title: Configuring Gateway Routes
description: Expose a physical network behind a TVPN node to every peer in your Workspace.
url: "https://docs.somewear.app/networking/gateway-routes/"
updated: "2026-08-28"
requires: {}
facets:
  hardware: []
  client:
    - tvpn:
        since: baseline
  satcom: []
---

A gateway is a TVPN node that bridges its overlay tunnel to a physical network, so remote peers can reach devices that don’t run TVPN — cameras, sensors, printers, or an entire LAN behind the gateway host.

Gateway routes let you:

- Expose a physical subnet to every peer in your Workspace
- Reach those devices by address, with no software installed on them
- Choose between translated addressing and advertising your real subnet as-is
- Have peers pick up the routes automatically, with no per-peer configuration

## Example

A vehicle carries a TVPN node with a camera subnet behind it. The operator wants remote peers to reach those cameras.

`config.toml`:

```toml
[gateway-routes]

# Map the camera subnet into TVPN's reserved overlay space.
[[gateway-routes.virtual]]
physical = "192.168.10.0/24"
virtual  = "100.127.10.0/24"
```

Restart the daemon. Every peer in the Workspace now has a route for `100.127.10.0/24` pointing at this node, and reaches camera `192.168.10.42` as `100.127.10.42`.

## Route types

There are two kinds of gateway route, and the difference is whether addresses get translated.

### Virtual routes

A virtual route maps a physical subnet onto a slice of TVPN’s reserved overlay range. Host bits are preserved; only the network bits change.

```toml
[[gateway-routes.virtual]]
physical = "192.168.10.0/24"
virtual  = "100.127.10.0/24"
```

```plaintext
Peers send to             Gateway forwards to
─────────────             ───────────────────
100.127.10.42        ⇄    192.168.10.42
100.127.10.99        ⇄    192.168.10.99
100.127.10.X         ⇄    192.168.10.X
```

The reverse translation runs automatically when the device replies.

**Use virtual routes by default.** They need no opt-in from peers, and because the overlay range is reserved they can’t collide with a peer’s own network.

#### Exposing a single device

Use a `/32` on both sides to map one address to one address. With no host bits left, the two addresses need no relationship to each other:

```toml
[[gateway-routes.virtual]]
physical = "192.168.1.227/32"
virtual  = "100.126.100.43/32"
```

Peers reach `192.168.1.227` at `100.126.100.43`, and nothing else on `192.168.1.0/24` is exposed. A `/24` route advertises 254 addresses whether or not anything is at them; a `/32` advertises exactly one device.

### Literal routes

A literal route advertises your real physical subnet onto the overlay with no translation. Peers send to the actual address.

```toml
[gateway-routes]
allow-literal = true
literal       = ["192.168.10.0/24"]
```

A peer reaches the camera at `192.168.10.42`, its real address.

**Use literal routes when** you manage your private address space carefully and would rather not have a second range to reason about.

> **Caution**
>
> This reroutes a real private subnet across the overlay. A peer that already has its own `192.168.10.0/24` would find that traffic pulled toward your gateway. That’s why literal routes require an explicit opt-in at both ends — see “Enabling literal routes” below.

## Configuration options

Gateway routes live in the daemon’s main config file, `config.toml`, under a `[gateway-routes]` table.

### Global options

| Field           | Type                 | Required | Default | Notes                                                                                                                                               |
| --------------- | -------------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allow-literal` | boolean              | no       | `false` | Enables literal routes. Required on the gateway advertising them **and** on every peer that wants to use them. See “Enabling literal routes” below. |
| `literal`       | list of CIDR strings | no       | empty   | Physical subnets to advertise onto the overlay with no translation. Ignored entirely unless `allow-literal = true`.                                 |
| `virtual`       | list of tables       | no       | empty   | Declared as repeated `[[gateway-routes.virtual]]` blocks, one per route.                                                                            |

### Virtual route fields

Each `[[gateway-routes.virtual]]` block takes two fields, both required.

| Field      | Type        | Required | Notes                                                                                        |
| ---------- | ----------- | -------- | -------------------------------------------------------------------------------------------- |
| `physical` | CIDR string | yes      | The real subnet on your network, e.g. `"192.168.10.0/24"`.                                   |
| `virtual`  | CIDR string | yes      | The overlay subnet peers send to, e.g. `"100.127.10.0/24"`. Must be inside `100.124.0.0/14`. |

### Constraints

| Rule                   | Detail                                                                                                                 |
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Virtual range          | Must fall inside `100.124.0.0/14` (`100.124.0.0`–`100.127.255.255`). This block is reserved for gateway translation.   |
| Matching prefix length | `physical` and `virtual` must use the same prefix length. A `/24` maps to a `/24`, never a `/25`.                      |
| Minimum virtual prefix | `/16` or longer.                                                                                                       |
| Minimum literal prefix | `/8` or longer.                                                                                                        |
| Literal range          | Must not overlap `100.64.0.0/10`, TVPN’s overlay address space.                                                        |
| No overlaps            | Virtual ranges may not overlap each other, and physical ranges may not overlap each other, across the whole route set. |
| Reload                 | Read once at startup. Restart the daemon after editing.                                                                |

### A complete example

```toml
[gateway-routes]
allow-literal = true

# Advertised as-is; peers reach 10.20.0.5 at 10.20.0.5.
literal = [
  "10.20.0.0/24",     # sensors
  "10.30.0.0/24",     # management
]

# Translated; peers reach 192.168.10.42 at 100.127.10.42.
[[gateway-routes.virtual]]
physical = "192.168.10.0/24"
virtual  = "100.127.10.0/24"

# A second translated subnet.
[[gateway-routes.virtual]]
physical = "192.168.20.0/24"
virtual  = "100.127.20.0/24"
```

Note the two different shapes: **literal routes are one list**, so additional subnets are extra entries in the `literal = [...]` array. **Virtual routes are repeated blocks**, so each one needs its own `[[gateway-routes.virtual]]` header with a `physical` and a `virtual` field.

### Enabling literal routes

`allow-literal` is a two-sided switch, off by default, and it must be `true` at **both** ends:

- **On the gateway**, it decides whether your own `literal` entries are advertised at all. Left at `false`, they’re dropped at startup with a warning in the log.
- **On a peer**, it decides whether that peer installs a route for a literal subnet some gateway advertised. Left at `false`, the advertisement is silently ignored and no route is created.

> **Note**
>
> If a peer can’t reach a gateway’s literal destinations, check `allow-literal` on the **peer** first — that side fails silently, with no log line, and it’s the more common oversight.

Virtual routes need no opt-in at either end.

## Operator setup

Beyond `[gateway-routes]` itself, three things must be true.

**1. IP forwarding on the gateway host**

TVPN writes the translated packet to its tunnel interface and lets the operating system route it onto your physical network — that hop only happens with IP forwarding enabled.

| OS      | How                                                                                                                      |
| ------- | ------------------------------------------------------------------------------------------------------------------------ |
| Linux   | Automatic — TVPN enables it at startup when gateway routes are configured.                                               |
| macOS   | `sysctl -w net.inet.ip.forwarding=1`                                                                                     |
| FreeBSD | `sysctl net.inet.ip.forwarding=1`, persisted via `/etc/sysctl.conf`                                                      |
| Windows | Enable Routing and Remote Access, or set `HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\IPEnableRouter` to `1` |

**2. The gateway can reach the physical network**

It needs an interface and a route into the subnet you’re exposing.

**3. The physical network can route replies back**

This is the step most often missed. TVPN doesn’t rewrite source addresses on the inbound path — a camera receives a packet from a source like `100.64.0.10` and tries to reply to it. For that reply to reach the gateway, the physical device or its upstream router needs a route:

```plaintext
100.64.0.0/10 via <gateway host's physical IP>
```

Hosts routed through the gateway need an MTU of `1320`, matching the VPN overlay network — a default `1500` can drop larger packets at the gateway. On Linux, use a route MTU:

```bash
ip route replace 100.64.0.0/10 via 192.168.10.2 mtu 1320
```

## Troubleshooting

TVPN starts normally even if your gateway routes are invalid — only gateway forwarding is affected, the rest of the daemon comes up.

**Validate before deploying:**

```bash
tvpn config validate
```

**Check what loaded** — on a peer, confirm the gateway is actually advertising what you expect:

```bash
tvpn peer get
```

Advertised routes appear under `Gateway Routes:` for each peer. `tvpn peer list` is the summary view and doesn’t include them.

**Log locations:**

| OS           | Path                                       |
| ------------ | ------------------------------------------ |
| Linux, macOS | `/var/lib/somewear/tvpn/log/tvpn.log`      |
| FreeBSD      | `/var/log/somewear/tvpn/tvpn.log`          |
| Windows      | `%ProgramData%\Somewear\tvpn\log\tvpn.log` |
| MikroTik     | Run `/container/log/print`                 |

### Interaction with QoS traffic policies

If you also use a traffic policy, packets from gateway-served devices are classified like any other outbound traffic. **For virtual routes**, classification runs before the source address is translated, so a rule matching `src-ip` sees the device’s real physical address, not the overlay address peers use:

```toml
# Matches the camera's actual address, not 100.127.10.42.
[[rule]]
priority = 15
match    = { src-ip = "192.168.10.42", proto = "udp" }
class    = "important"
```

## Related

- [Quality of Service](https://docs.somewear.app/networking/qos/) — prioritize traffic across multiple network links, including gateway-served traffic
- [Install TVPN](https://docs.somewear.app/networking/install/linux/) — set up TVPN before configuring gateway routes
