> ## Documentation Index
> Fetch the complete documentation index at: https://www.latitude.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Block storage

> High performance block volumes you map to a bare metal server and use over NVMe/TCP

Block storage gives you persistent, high performance volumes for bare metal servers. A volume lives independently of any server: you create it in a project, map it to one of your servers, and the server reaches it over a private storage network using NVMe/TCP. Data stays on the volume when you unmap it, so you can move it to another server in the same location.

## Prerequisites

* A verified team and a project in a location where high performance storage is available. The create flow only lists those locations.
* A bare metal server in the same project and location as the volume, bonded onto a [public network](/docs/networking/public-networks). Bonding gives the server the redundant `bond0` link the storage network is delivered on. See [Network bonding](/docs/servers/network-bonding).
* One of the [supported operating systems](#supported-operating-systems) if you want the server to be configured automatically.

## Creating a volume

<Steps>
  <Step title="Open the create flow">
    In the dashboard, go to **Storage** > **Block storage** and click **Create volume**.
  </Step>

  <Step title="Choose a location and size">
    Pick the location and set the capacity. The volume must be in the same location as the server you plan to map it to.
  </Step>

  <Step title="Create">
    Click **Create volume**. The volume opens on its detail page, ready to be mapped.
  </Step>
</Steps>

## Mapping a volume to a server

Mapping connects the volume to one server. The server must be in the volume's project and location.

<Steps>
  <Step title="Pick a server">
    On the volume page, open the server picker under **Assigned server**. Servers already on a public network show a **Bonded** badge and can be selected. Servers that support bonding but aren't on a public network yet show **Reinstall to bond** and can't be selected until you [bond them](/docs/servers/network-bonding). Servers that can't be bonded aren't listed.
  </Step>

  <Step title="Assign">
    Click **Assign**. The status shows **Mapping** while the volume is being attached, then **Mapped**. If the mapping fails, pick a server and try again.
  </Step>
</Steps>

<Note>
  If the volume's location has no public network in your project yet, the volume page offers to create one before you can pick a server.
</Note>

## Activating storage networking on the server

Once the volume is mapped, the server needs a storage VLAN on top of its bond and an NVMe/TCP connection to the volume. You can let Latitude.sh configure this, or do it yourself. Both options appear on the volume page after you [map the volume to a server](#mapping-a-volume-to-a-server), once its status shows **Mapped**.

### Option 1: Reinstall

Click **Reinstall** on the volume page. Reinstalling with a [supported operating system](#supported-operating-systems) configures the storage VLAN, the routes and the NVMe/TCP connection automatically, and connects the volume on boot.

<Warning>
  Reinstalling erases the server's disks. Back up anything you need first.
</Warning>

### Option 2: Configure manually

If you don't want to reinstall, click **View guide** on the volume page. The guide lists the commands for your server and fills in the values the platform already knows, such as the storage VLAN id. Anything still shown as a placeholder, such as the storage IP or gateway, comes from Latitude support; replace it before applying. In short:

<Steps>
  <Step title="Add the storage VLAN on top of the bond">
    Create a netplan file that defines a VLAN on `bond0` with the storage IP and the routes to the storage networks, then apply it with `sudo netplan generate` and `sudo netplan apply`.
  </Step>

  <Step title="Run the storage setup script">
    Download and run `lsh-block-setup.sh`. It installs the NVMe/TCP tooling, sets the server's NVMe host name, and seeds the discovery target. Review the script before running it; it lists its changes and only acts with your approval.
  </Step>

  <Step title="Connect the volume">
    Run `sudo nvme connect-all`. The volume appears as an NVMe device on the server.
  </Step>
</Steps>

<Warning>
  The manual path changes live networking on the server. Keep an out-of-band session open as a fallback.
</Warning>

## Using the volume

After connecting, the volume shows up as a new NVMe device. Device numbers such as `nvme1n1` change between servers and can point at a local disk, so identify the volume before touching it:

```bash theme={null}
sudo nvme list
lsblk -o NAME,SIZE,MODEL,SERIAL,MOUNTPOINTS
```

The mapped volume is the NVMe/TCP device whose size matches the volume you created. Format and mount it using its stable path under `/dev/disk/by-id/`, and keep that path for `/etc/fstab` so a device renumbering after reboot can't mount the wrong disk:

```bash theme={null}
VOLUME=/dev/disk/by-id/nvme-example-model_example-serial
ls -l "$VOLUME"
sudo mkfs.ext4 "$VOLUME"
sudo mkdir -p /mnt/volume
sudo mount "$VOLUME" /mnt/volume
```

Set `VOLUME` to the entry under `/dev/disk/by-id/` that matches the serial you found. The `ls` line confirms it points at the device you expect.

<Warning>
  Formatting erases the device. Double-check the size and serial before running `mkfs`.
</Warning>

## Performance

Measured with `fio` and direct I/O on the unformatted volume, from a server with a 2×10 Gbps bond:

| Workload | Result |
| - | - |
| Sequential read, 1 MiB | 2.3 GB/s |
| Sequential write, 1 MiB | 2.1 GB/s |
| Random read, 4 KiB | 460K IOPS |
| Random write, 4 KiB | 100K IOPS |

Results depend on the server's network capacity, the filesystem and the workload.

Traffic to the volume uses both links of the bond with no tuning. At these rates, sequential throughput and random reads are limited by the server's network.

These results use many parallel requests. The random read result needs about 1,000 requests in flight, for example 16 threads at a queue depth of 64. A single 4 KiB request takes about 0.4 ms to read and 0.9 ms to write, similar to [file storage](/docs/storage/filesystem#performance).

## Expanding a volume

Volumes can grow but not shrink. Use **Expand** in the volume's actions menu and pick a new size, up to 20 TB. Volumes can be expanded while mapped, and the server keeps running. Billing is prorated to the new size from the day you expand. If **Expand** doesn't appear for a volume, that volume can't be resized; contact support.

After the resize, set `VOLUME` to the volume's path under `/dev/disk/by-id/`, as in [Using the volume](#using-the-volume), and confirm the server sees the new size:

```bash theme={null}
VOLUME=/dev/disk/by-id/nvme-example-model_example-serial
lsblk -o NAME,SIZE "$VOLUME"
```

If it still shows the old size, list the volume's NVMe/TCP controllers:

```bash theme={null}
sudo nvme list-subsys "$(readlink -f "$VOLUME")"
```

Each line starting with `+-` names one of the volume's controllers, such as `nvme10`. Rescan any of them, replacing `<controller>` with its name:

```bash theme={null}
sudo nvme ns-rescan /dev/<controller>
```

Then grow the filesystem to use the new space. For ext4, this works while the volume is mounted:

```bash theme={null}
sudo resize2fs "$VOLUME"
```

For XFS, run `sudo xfs_growfs /mnt/volume` instead.

## Unmapping and deleting

To move a volume to another server, release it on the server first: stop anything using the mount, unmount it, and remove its `/etc/fstab` entry so the next boot doesn't wait for a device that is no longer there.

```bash theme={null}
sudo umount /mnt/volume
```

Then click **Unmap server** on the volume page. The status shows **Unmapping** until the volume is detached. Data on the volume is preserved and you can map it to another server in the same location.

To delete a volume, unmap it and use **Delete volume** in the volume's actions menu. Deleting a volume permanently removes its data.

## Supported operating systems

Automatic configuration on reinstall is available for these images:

* Debian 13
* Ubuntu 24.04 and 26.04
* AlmaLinux 9 and 10
* Rocky Linux 9 and 10

On other operating systems, use the manual guide.

## Current limits

* Map one block volume per server for now.
* Volumes can be expanded but not shrunk. Create a new volume if you need a smaller one.
* A volume can only be mapped to a server in the same project and location.
* Automatic configuration requires a reinstall with a supported operating system. On other images or without a reinstall, follow the manual guide.

These limits are being worked on and will be lifted in upcoming releases.

## Pricing

Block storage costs \$0.08 per GB per month, billed on provisioned capacity while the volume exists, whether or not it is mapped. The total for the size you choose is shown in the create flow before you confirm. See the [storage pricing page](https://www.latitude.sh/pricing/storage) for details.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.