---
title: IdahoMesh VPN — Device Setup
type: runbook
tags:
  - mesh
aliases: []
related:
  - [[idahomesh-bridge-setup]]
  - [[meshtastic-headscale-runbook]]
  - [[meshtastic-sidecar-node]]
  - [[headscale-onboard-node]]
  - [[caddy]]
updated: 2026-06-18
---
# IdahoMesh VPN — Device Setup

Join a device to the IdahoMesh tailnet (Meshtastic mesh network VPN).

---

## Overview

| Item | Value |
|------|-------|
| Headscale URL | `https://vpn.idahomesh.com` |
| Tailnet prefix | 100.100.0.0/16 |
| MagicDNS domain | mesh.local |
| Supported platforms | Linux (x86/ARM), Windows, macOS, Android, iOS |

### Users

| User | Purpose | Key type |
|------|---------|----------|
| malice | Echo6 infrastructure (bridge LXC) | Short-lived, single-use |
| sidpatchy | Sidpatchy's devices | Short-lived, single-use |
| nebra | Nebra CM3 gateways (field devices) | Long-lived, reusable |

---

## Prerequisites

Before starting, you need:

1. **A preauthkey** — generated by the IdahoMesh Headscale admin (see [Generating Keys](#generating-preauthkeys) below)
2. **Internet access** on the device (to reach vpn.idahomesh.com)
3. **Root/admin access** on the device

---

## Linux (Debian/Ubuntu/Raspberry Pi OS)

### Install Tailscale

```bash
curl -fsSL https://tailscale.com/install.sh | sh
```

### Join the IdahoMesh tailnet

```bash
tailscale up \
  --login-server=https://vpn.idahomesh.com \
  --authkey=<YOUR_PREAUTHKEY> \
  --hostname=<DEVICE_NAME>
```

Replace:
- `<YOUR_PREAUTHKEY>` — the key provided to you
- `<DEVICE_NAME>` — short, lowercase hostname (e.g., `burley-butte`, `sid-laptop`)

### Verify

```bash
tailscale status
tailscale ip -4
ping -c 3 100.100.0.1  # Ping another node on the tailnet
```

You should see a `100.100.x.x` IP assigned.

---

## Nebra CM3 Gateway (ARM/Raspberry Pi)

Same as Linux above. The install script auto-detects ARM.

```bash
# Install
curl -fsSL https://tailscale.com/install.sh | sh

# Join — use the reusable nebra preauthkey
tailscale up \
  --login-server=https://vpn.idahomesh.com \
  --authkey=<NEBRA_PREAUTHKEY> \
  --hostname=<GATEWAY_NAME>
```

**Naming convention for Nebra gateways:**

| Gateway | Hostname |
|---------|----------|
| Burley Butte | `burley-butte` |
| Picabo | `picabo` |
| AIDA-NEBRA | `aida-nebra` |

### Enable Tailscale on boot

Tailscale installs as a systemd service and starts on boot automatically. Confirm:

```bash
systemctl is-enabled tailscaled
# Should output: enabled
```

---

## Windows

### Install

Download and install Tailscale from: https://tailscale.com/download/windows

### Join

Open **PowerShell as Administrator:**

```powershell
tailscale up --login-server=https://vpn.idahomesh.com `
  --authkey=<YOUR_PREAUTHKEY> `
  --hostname=<DEVICE_NAME>
```

### Verify

```powershell
tailscale status
tailscale ip -4
```

---

## macOS

### Install

Download and install Tailscale from: https://tailscale.com/download/mac

Or via Homebrew:

```bash
brew install tailscale
```

### Join

```bash
tailscale up \
  --login-server=https://vpn.idahomesh.com \
  --authkey=<YOUR_PREAUTHKEY> \
  --hostname=<DEVICE_NAME>
```

### Verify

```bash
tailscale status
tailscale ip -4
```

---

## Android

1. Install Tailscale from **F-Droid** (recommended — supports custom servers) or Google Play
2. Open Tailscale → tap the three-dot menu → **Use custom coordination server**
3. Enter: `https://vpn.idahomesh.com`
4. Authenticate (it will open a browser if no authkey is used)

> **Note:** The Google Play version may not support custom coordination servers. Use the F-Droid build if the option is missing.

---

## iOS

1. Install Tailscale from the App Store
2. Open Tailscale → Settings → **Use Alternate Server**
3. Enter: `https://vpn.idahomesh.com`
4. Authenticate via browser

> **Note:** iOS support for custom Headscale servers can be limited. If the option is unavailable, use the CLI on another device and share connectivity via subnet routing.

---

## Verification Checklist

Run from the newly joined device:

```bash
echo "=== IdahoMesh VPN Check ==="
echo "Hostname:     $(hostname)"
echo "Tailscale IP: $(tailscale ip -4 2>/dev/null || echo 'N/A')"
echo "Status:       $(tailscale status --self 2>/dev/null | head -1 || echo 'NOT CONNECTED')"
echo "Login server: $(tailscale debug prefs 2>/dev/null | grep -o 'ControlURL:[^ ]*' || echo 'unknown')"
```

Expected: a `100.100.x.x` IP and connected status.

### Test connectivity to other nodes

```bash
# List all visible peers
tailscale status

# Ping another node by MagicDNS name
tailscale ping <OTHER_HOSTNAME>

# Or by IP
ping 100.100.0.x
```

---

## Generating Preauthkeys

**Admin only** — run on the IdahoMesh Headscale server (CT 106, 192.168.1.106):

```bash
# For a human user (single-use, expires in 24h)
headscale preauthkeys create --user <USERNAME> --expiration 24h

# For a human user (single-use, longer window)
headscale preauthkeys create --user <USERNAME> --expiration 72h

# For Nebra gateways (reusable, long-lived)
headscale preauthkeys create --user nebra --reusable --expiration 8760h

# List existing keys
headscale preauthkeys list --user <USERNAME>
```

Users: `malice` (ID 4), `sidpatchy` (ID 2), `nebra` (ID 3)

> **Note:** Headscale v0.28.0 `--user` flag requires user IDs (integers), not names.

---

## Removing a Device

**Admin only:**

```bash
# List all nodes
headscale nodes list

# Delete a node by ID
headscale nodes delete -i <NODE_ID>
```

On the device itself:

```bash
tailscale logout
```

---

## Troubleshooting

### "connection refused" or timeout on join

- Confirm the device has internet access: `curl -I https://vpn.idahomesh.com`
- Check [[dns]] resolution: `dig vpn.idahomesh.com`
- Verify the preauthkey hasn't expired

### "key expired" or "invalid key"

- Preauthkeys are time-limited. Ask the admin for a new one
- Nebra keys are reusable but still expire — check with `headscale preauthkeys list --user nebra`

### Device shows "offline" in node list

- Check if tailscaled is running: `systemctl status tailscaled`
- Restart: `systemctl restart tailscaled`
- Force re-auth: `tailscale up --login-server=https://vpn.idahomesh.com --force-reauth`

### Can't reach other nodes

- Confirm both devices show as "online" in `tailscale status`
- Check ACL policy — the admin may need to add rules for your user group
- Try direct IP ping before MagicDNS names

### MagicDNS not resolving

- Confirm `--accept-dns=true` (this is the default)
- Check: `tailscale debug prefs | grep CorpDNS`
- Restart tailscaled

---

## Quick Reference

| Item | Value |
|------|-------|
| Headscale URL | `https://vpn.idahomesh.com` |
| Tailnet prefix | 100.100.0.0/16 |
| MagicDNS domain | mesh.local |
| Headscale server | CT 106 on utility (192.168.1.106) |
| Admin access | SSH to 192.168.1.106 or via utility Proxmox |
| ACL policy | `/etc/headscale/acl.json` on CT 106 |

---

*Last updated: 2026-02-11*
