Skip to content

Adding a public node

This guide walks through adding a VM/VPS/root server/Droplet with a static IP from your Mac: create the VM, prepare SSH, fill the environment inventory, and open firewall rules.

The example uses the dev environment (yours might be prod, staging, or another name) and host name static-1.

Where you see ENV=<env>, replace with your environment name (for example prod).

Know which environment and inventory file you are completing, and have an external Postgres instance ready for that environment (configured in the vault before the first deploy).

You must be arriving here from Get started locally or Deploy to production.

Read project from inventories/<env>/hosts.ymlall.vars.project (currently example). In examples below, <project> is that value and <env> is the environment name you pass as ENV= (for example dev, prod).

Our objective is to complete inventories/<env>/hosts.yml — for dev, that is inventories/dev/hosts.yml:

all:
vars:
project: example
hostname: example.com
nodes:
hosts:
static-1:
public_ip: "xxx.xxx.xxx.xxx"
ssh_ed25519_sha256: "SHA256:0f37...2ed230"
private_address: 10.217.80.11

For a Lima guest or a home-lab roaming node, see the other start-here and roaming guides — this page is public static nodes only.

Create the Ed25519 key Ansible will use from your Mac.

On your Mac, create an Ed25519 key with an empty passphrase. Store both files in your password manager.

Terminal window
ssh-keygen -t ed25519 -a 100 \
-f ~/.ssh/example-dev \
-C "example dev"
ssh-add ~/.ssh/example-dev
pbcopy < ~/.ssh/example-dev.pub

Inventory will reference this key as ~/.ssh/example-dev via ssh_private_key_file in group vars.

Important: We will need the public key from Step 1 in Step 3.

Provision an Ubuntu amd64 VM with a public IPv4 address.

In your cloud provider console:

  1. Create a Ubuntu Server 26.04 amd64 (x86_64) on your favourite cloud provider. arm64 may/may not work, but we have not tested it.
  2. Attach a public IPv4 (or a stable DNS name you will put in hosts.yml).
  3. Do not rely on this automation to create the server or provider firewall rules—you configure those yourself.

Note the public IP (example format xxx.xxx.xxx.xxx).

Step 3: Prepare the node (console or provider SSH)

Section titled “Step 3: Prepare the node (console or provider SSH)”

Create the ops user, install your public key, and grant passwordless sudo.

On the new VM, create the inventory SSH user (example ops), install the Mac public key, and grant passwordless sudo:

Terminal window
sudo adduser --disabled-password --gecos '' ops
sudo install -d -o ops -g ops -m 0700 /home/ops/.ssh
sudo tee /home/ops/.ssh/authorized_keys >/dev/null

Paste the public key from the clipboard (from Step 1), press Control + DControl + D, then:

Terminal window
sudo chown ops:ops /home/ops/.ssh/authorized_keys
sudo chmod 600 /home/ops/.ssh/authorized_keys
printf '%s\n' 'ops ALL=(ALL) NOPASSWD:ALL' | \
sudo tee /etc/sudoers.d/90-sc-ops >/dev/null
sudo chmod 440 /etc/sudoers.d/90-sc-ops
sudo visudo -cf /etc/sudoers.d/90-sc-ops
sudo -u ops sudo -n true

visudo -cf should report parsed OK.

Confirm the authorized key on the VM matches your Mac key.

On your Mac:

Terminal window
ssh-keygen -lf ~/.ssh/example-dev.pub

On the new VM:

Terminal window
sudo ssh-keygen -lf /home/ops/.ssh/authorized_keys

The SHA256:… lines must match.

Copy the VM host-key fingerprint for inventory pinning.

On the VM:

Terminal window
sudo ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub
# Sample output: 256 SHA256:pRR2cezc6UOTGq+vTrVwPFCFKlHRKuuV/OL/9/c3e+M root@whatever-server (ED25519)

Copy the full SHA256:pRR...e+M value into your password manager. You will paste it into hosts.yml as ssh_ed25519_sha256 (this is the VM/server’s fingerprint).

Add the node’s public IP and fingerprint to your hosts inventory.

Edit inventories/<env>/hosts.yml. Under nodes.hosts, set static-1 (or add a new static-2, …):

static-1:
public_ip: "159.203.27.69"
ssh_ed25519_sha256: "SHA256:pRR...e+M"
private_address: 10.217.80.11

To add another public node, duplicate the block as static-2, assign the next mesh IP (e.g. .12), add an A record for ns.<hostname> pointing at that node’s public IPv4, and run task up ENV=<env> again. If the DNS is hosted on Cloudflare, do not enable the proxy orange icons.

Open TCP 22 for bootstrap and UDP 51830 if roaming peers will dial in later.

On the cloud provider (or host) firewall for this VM:

Phase Rule
Before setup Allow inbound TCP 22 from your Mac’s current public /32
After setup Allow inbound UDP 51830 from peers that will dial this hub (if roaming nodes are added later, open UDP 51830 broadly—not only the Mac /32)
After setup Allow inbound TCP 80 and TCP 443

Your Mac does not need further configuration for this step.

Confirm direct SSH from your Mac works before continuing.

From the Mac, enter the following command with the path to the private key you created in Step 1 and the public IP of the VM:

Terminal window
ssh -i ~/.ssh/example-dev -o IdentitiesOnly=yes [email protected] true

It will ask for “Are you sure you want to continue connecting (yes/no)?” — type yes and press EnterEnter.

If this fails, fix user, keys, or firewall before continuing.

Go back to Get started locally or Deploy to production from where you started this guide.

To add a home-lab roaming node later, see Adding a roaming node.