# CLI reference

> Every command and flag of the command-line tool, with what it prints.

The command-line tool is `underpass` on macOS and Linux (with `tailnet` as a second name) and `tailnet.exe` on Windows, in `C:\Program Files\Tailnet`. This page writes it as `underpass`.

```text
underpass <command> [flags]
```

Run `underpass <command> -h` for a command's flags. Errors are printed as `tailnet: <message>` and exit with status 1.

## How commands reach the service

When the background service is installed (Windows installer, or the macOS and Linux install command), `up`, `down`, `status`, `logout`, `set` and `serve` talk to it. You don't need `sudo` for that:

- **Linux:** after the installer adds you to the `tailnet` group (log out and back in once)
- **macOS and Windows:** for the signed-in user

Without the service, or when you pass a flag that only a standalone run uses (`--auth-key`, `--state-dir`, `--config-only`, `--interface`, `--port`, `--engine`, `--hostname`), `up` runs the tunnel itself, in the foreground, until you stop it. That needs `sudo` or an administrator prompt.

## Commands

| Command | Does |
|---|---|
| [`up`](#up) | Connect: sign in through the browser, or join with `--auth-key` |
| [`status`](#status) | Show this machine and the other devices |
| [`down`](#down) | Disconnect; the machine stays registered |
| [`logout`](#logout) | Disconnect and forget this machine's login |
| [`run`](#run) | Keep the tunnel up using the saved registration, with no service |
| [`set`](#set) | Subnet routes and exit nodes |
| [`serve`](#serve) | Host a [service](/features/services) port |
| [`cert`](#cert) | Get an HTTPS certificate for this machine's name |
| [`service`](#service) | Install, remove or check the background service (Windows, macOS) |
| [`uninstall`](#uninstall) | Remove the app (Windows, macOS) |
| [`daemon`](#daemon) | Run the background service (used by the service itself) |
| [`version`](#version) | Print the version |

## up

Connects this machine.

```bash
underpass up
```

Through the service, a machine that isn't signed in prints a link and waits:

```text
To add this machine to your tailnet, open:

	https://app.getunderpass.com/…

Waiting for approval...
Connected as laptop (100.64.0.7).
```

If [device approval](/manage/machines#device-approval) is on, it says `Logged in. An admin needs to approve this machine in the dashboard...` until an admin does.

| Flag | Default | Description |
|---|---|---|
| `--auth-key` | `$TAILNET_AUTH_KEY` | Join with an [auth key](/manage/auth-keys) instead of the browser. Runs standalone |
| `--hostname` | The machine's hostname | Name to register with |
| `--server` | `https://coord.getunderpass.com` | Coordination server URL (or `$TAILNET_SERVER`) |
| `--engine` | `userspace` | `userspace`: built-in WireGuard with direct connections and relay fallback. `system`: the OS's WireGuard (Linux, Windows), direct connections only |
| `--interface` | `tailnet0` | Tunnel interface name |
| `--port` | `41642` | WireGuard listen port |
| `--state-dir` | See [State](#state) | Where keys and the login are stored |
| `--config-only` | `false` | Only write a WireGuard config file; needs no admin rights |
| `--advertise-routes`, `--advertise-exit-node`, `--exit-node`, `--accept-routes` | | Same as [`set`](#set) |

## status

```bash
underpass status
```

```text
100.64.0.7  laptop.underpass.internal  connected  (https://coord.getunderpass.com)
Relay: https://relay… (connected)

ADDRESS      NAME       OS       PATH                     LATENCY  HANDSHAKE  ROUTES
100.64.0.21  db-prod    linux    direct 203.0.113.7:41642  11.2 ms  4s ago     -
100.64.0.12  pi-garage  linux    relay                    38.6 ms  9s ago     192.168.1.0/24
```

The first line's state is one of `connected`, `connecting`, `disconnected`, `waiting for login`, `logged out` or `waiting for an admin to approve this machine`. A `Problem:` line explains anything that's wrong.

| Column | Shows |
|---|---|
| `PATH` | `direct <address:port>`, `relay`, or `offline` |
| `LATENCY` | Round trip, like `11.2 ms` |
| `HANDSHAKE` | Time since the last WireGuard handshake |
| `ROUTES` | What the device offers: `exit node`, subnets, or `-` |

## down

```bash
underpass down
```

Prints `Disconnected. Run 'underpass up' to reconnect.` The machine stays in your network.

## logout

```bash
underpass logout
```

Signs this machine out and forgets its login. Its keys are kept, so signing in again brings back the same name and address. Prints `Logged out. Run 'underpass up' to log in again.`

## run

```bash
sudo underpass run
```

Keeps the tunnel up using a saved registration, without the service, for example under your own systemd unit after `up --auth-key` (see [Add a server](/getting-started/add-a-server#with-an-auth-key)). Takes `--state-dir`, `--engine` and `--config-only`. Without a saved registration it says `not registered: run 'underpass up --auth-key ...' first`.

## set

Changes routing. A running client picks changes up within a few seconds.

| Flag | Default | Description |
|---|---|---|
| `--advertise-routes` | none | Subnets to route for your network, comma-separated (`192.168.1.0/24`). `""` stops. An admin approves them. See [Subnet routes](/features/subnet-routes) |
| `--advertise-exit-node` | `false` | Offer this machine as an [exit node](/features/exit-nodes) |
| `--exit-node` | none | Send internet traffic through this machine (its name or address). `""` goes direct |
| `--accept-routes` | `true` | Use subnets other machines route |

Advertising routes and exit nodes works on Linux, macOS and Windows with the default engine. With no flags, `set` prints the current settings.

## serve

Hosts a port of a [service](/features/services) on this machine.

```bash
underpass serve --service=svc:grafana --tcp=443 127.0.0.1:3000   # start
underpass serve --service=svc:grafana --tcp=443 off              # stop
underpass serve status                                           # list
```

| Flag | Description |
|---|---|
| `--service` | The service, like `svc:web`. It must exist in the dashboard first |
| `--tcp` | The service's port, like `443` |

The last argument is where the app listens: a port (`3000`, meaning `127.0.0.1:3000`) or `host:port`. The machine serves once an admin approves it under **Services**.

## cert

Gets a trusted HTTPS certificate for this machine's full name. See [HTTPS certificates](/features/https).

| Flag | Description |
|---|---|
| `--cert-file` | Also copy the certificate (PEM chain) here |
| `--key-file` | Also copy the private key (PEM) here |
| `--state-dir` | Where the machine's login is stored |

## service

Windows (administrator) and macOS (`sudo`):

```bash
underpass service install     # install and start the background service
underpass service uninstall   # remove it; keys are kept
underpass service status      # installed / running / not installed
```

On Linux the service is the systemd unit `tailnetd`: `sudo systemctl enable --now tailnetd`.

## uninstall

```bash
underpass uninstall [--remove-keys]
```

Removes the app on Windows and macOS. `--remove-keys` also deletes this machine's keys. On Linux, see [Install](/getting-started/install#uninstall).

## daemon

Runs the background service. You don't run this yourself; the installed service does.

| Flag | Default | Description |
|---|---|---|
| `--state-dir` | | Where keys and the login are stored |
| `--listen` | `127.0.0.1:41112` (Windows), `127.0.0.1:41113` (macOS, Linux) | Local API address, localhost only |
| `--token-file` | | Where the local API token is written for the apps |

## version

```bash
underpass version
```

Prints the name and version, like `underpass 0.7.0`.

## State

Each machine keeps its keys and login in a state folder, readable only by the system:

| | Background service | Standalone (`up`, `run`, `cert`) |
|---|---|---|
| Linux, macOS | `/var/lib/tailnetd` | `/var/lib/tailnet` (as root) |
| Windows | `%ProgramData%\TailnetService` | `%ProgramData%\tailnet` |

## Environment variables

| Variable | Used by | Meaning |
|---|---|---|
| `TAILNET_AUTH_KEY` | `up` (standalone) | Default for `--auth-key` |
| `TAILNET_SERVER` | `up` | Default for `--server` |
| `UNDERPASS_NO_UP=1` | Install command | Install without connecting |
| `UNDERPASS_DOWNLOAD` | Install command | Download from another base URL |
