Storage Azure

ZeroFS on Ubuntu 24.04 on Azure User Guide

| Product: ZeroFS on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of ZeroFS on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. ZeroFS is an open source, log structured filesystem for object storage, written in Rust. It presents a bucket or container as a real POSIX filesystem and serves it over NFS, 9P and NBD, so ordinary tools, ordinary mounts and ordinary block devices work against object storage without modification.

File contents are split into 32 KiB extents. Each extent is compressed, encrypted and packed into immutable segment objects, while metadata (inodes, directory entries and one pointer per extent) lives in an LSM tree on the same store. A local disk and memory cache keeps working set access fast.

The image installs the official pinned ZeroFS 2.3.2 package from the project's own signed repository and runs it under systemd as the unprivileged zerofs system user. Because ZeroFS is a filesystem for object storage, it cannot do anything without a backend, so this appliance bundles one and wires it up for you: a single node object store provides the S3 API, and Redis provides the conditional write coordination that ZeroFS requires for fencing. The result is a filesystem that works the moment the virtual machine boots, and that you can repoint at Azure Blob Storage, Amazon S3, Google Cloud Storage or any S3 compatible store whenever you are ready.

Secure by default, and for ZeroFS this matters more than usual. The ZeroFS Web UI has no authentication on any route: its WebSocket endpoint hands the browser a full read and write session on the filesystem, its administrative gRPC endpoint accepts requests from any origin, and it includes an in browser terminal that boots a Linux virtual machine with the filesystem mounted inside it. NFS, 9P and NBD are likewise unauthenticated wire protocols. This image therefore exposes none of them to the network. Every one of those listeners is bound to loopback, and the only network facing surface is nginx on port 443, which terminates TLS and enforces HTTP basic authentication before anything reaches ZeroFS, including the WebSocket. This is exactly what the ZeroFS project itself recommends.

Every secret is generated on your own virtual machine at first boot and none is baked into the image: the filesystem encryption password, the web credential, the object store keys, the Redis password and the TLS certificate. Until first boot has produced them, ZeroFS and the object store are both prevented from starting at all, so the shipped image can never come up with a shared or empty encryption password.

What is included:

  • ZeroFS 2.3.2 installed unmodified from the project's official signed package repository, version pinned so an unattended upgrade cannot move you to an untested release

  • NFS, 9P and NBD servers, the web file manager, a live dashboard with a file access tracer, and the in browser terminal

  • A bundled single node object store and a Redis conditional write coordinator, both bound to loopback, so the filesystem works out of the box

  • The filesystem already mounted and ready to use on the virtual machine at /mnt/zerofs

  • A dedicated 40 GiB data volume at /srv/zerofs holding the object store and the cache

  • nginx terminating TLS on :443, enforcing HTTP basic authentication on every request including the WebSocket, with HSTS and sensible security headers; port :80 redirects to HTTPS

  • A unique credential and encryption password generated per VM on first boot; only a one way bcrypt hash of the web password is stored, and the plain values land in root only files

  • A built in self test at /usr/local/bin/zerofs-selftest that proves the authentication gate and a real filesystem round trip end to end

  • Ubuntu 24.04 LTS base with the latest security patches applied at build time and unattended security upgrades left enabled

  • Azure Linux Agent for seamless cloud integration and SSH key injection

  • 24/7 cloudimg support with a guaranteed 24 hour response SLA

cloudimg is not affiliated with the ZeroFS project. ZeroFS is licensed under the GNU Affero General Public License v3.0 and is shipped here unmodified.

Prerequisites

  • An Azure subscription with permission to deploy virtual machines

  • An SSH key pair for administrative access to the VM as the azureuser account

  • A Network Security Group allowing inbound TCP 443 (HTTPS) and 80 (redirect) from the networks that should reach the service, and 22 (SSH) from your management network only

  • A recommended size of Standard_B2s or larger

Step 1: Deploy from the Azure Portal

  1. Locate the ZeroFS on Ubuntu 24.04 LTS image in the Azure Marketplace and select Create.

  2. Choose your subscription, resource group and region.

  3. Select a VM size (Standard_B2s or larger) and provide your SSH public key for the azureuser account.

  4. On the Networking tab, allow inbound 443 and 80 from the networks that should reach the service, and 22 from your management network only.

  5. Leave the data disk that ships with the image in place. It is mounted at /srv/zerofs and holds the object store and cache.

  6. Review and create. First boot takes about a minute while the virtual machine generates its own secrets and initialises the filesystem.

Step 2: Deploy from the Azure CLI

az group create --name zerofs-rg --location eastus

az vm create \
  --resource-group zerofs-rg \
  --name zerofs-vm \
  --image cloudimg:zerofs-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

az vm open-port --resource-group zerofs-rg --name zerofs-vm --port 443 --priority 1001
az vm open-port --resource-group zerofs-rg --name zerofs-vm --port 80 --priority 1002

Step 3: Retrieve your per instance credential

Every virtual machine generates its own web credential on first boot. It is written to a root only file:

sudo cat /root/zerofs-credentials.txt

The file names the administrator username, the password and the URL to open:

ZEROFS_UI_USER=admin
ZEROFS_UI_PASSWORD=<a unique 24 character password generated on this VM>
ZEROFS_URL=https://<your VM address>/
ZEROFS_MOUNTPOINT=/mnt/zerofs

The per VM zerofs-credentials.txt file shown over SSH with the administrator username and URL visible and the password value masked, alongside the zerofs.env secrets file, all generated uniquely on this VM's first boot

Back up the encryption password. ZEROFS_PASSWORD in /etc/zerofs/zerofs.env is the key that encrypts everything stored in the object store. Without it the stored data cannot be decrypted, and cloudimg cannot recover it for you. Copy it somewhere safe before you put real data on this filesystem:

sudo grep '^ZEROFS_PASSWORD=' /etc/zerofs/zerofs.env

Step 4: Sign in to the Web UI

Open https://<your VM address>/ in a browser. The certificate is self signed and generated for this virtual machine, so your browser will warn once; accept it, or replace the certificate as described in Step 12. Sign in with the username and password from Step 3.

You land on the file manager, showing the real contents of the ZeroFS filesystem:

The ZeroFS web file manager listing the root of the filesystem, showing the README.txt file, the documents directory and the .nbd directory with their POSIX modes, owners, sizes and timestamps

Step 5: Browse, open and edit files

Double click a file to open it. Text files open in an editor and Ctrl+S writes the buffer back through the filesystem. Drag and drop uploads work, including whole folders, and dragging entries onto another tab performs a move rather than a copy.

Opening the seeded README.txt reads its bytes back out of the encrypted object store and displays them:

README.txt opened in the ZeroFS web editor, showing the file's real contents read back out of the encrypted object store, with the file manager listing still visible alongside it

Ctrl+F searches the current directory, recursing up to ten levels. A plain query is a case insensitive substring match, a query containing * or ? is a glob, and /pattern/ is a regular expression.

Step 6: Watch the dashboard

The Monitor view streams live statistics: total operations, bytes read and written, storage used, inode count, throughput and IOPS, plus a file access tracer showing recent operations.

The ZeroFS dashboard showing live statistics including total operations, bytes read, bytes written, storage used and inodes used, with throughput and IOPS charts streaming underneath

Step 7: Use the in browser terminal

The Terminal view boots a small Linux virtual machine inside your browser with the ZeroFS filesystem mounted at /mnt. It is useful for quick file operations without opening an SSH session. The guest has no network device, so files move in and out only through the mounted filesystem.

The ZeroFS in browser terminal after booting, listing the ZeroFS filesystem at /mnt and printing the contents of a file read from it

Because this terminal can read and write everything on the filesystem, it is one of the main reasons the Web UI is never exposed without authentication on this image.

Step 8: Use the filesystem on the virtual machine

ZeroFS is already mounted at /mnt/zerofs over the in kernel 9P client. Treat it like any other filesystem:

ls -la /mnt/zerofs/
mount | grep ' /mnt/zerofs '
echo "hello from the cloudimg deployment guide" | sudo tee /mnt/zerofs/hello.txt
sync
cat /mnt/zerofs/hello.txt
sudo rm -f /mnt/zerofs/hello.txt

The mount reports no fixed size, because the capacity of a ZeroFS filesystem is whatever its object store provides. On this image the bundled store is bounded by the 40 GiB data volume; check the real backing capacity with:

df -h /srv/zerofs

Step 9: Create a block device over NBD

Files placed in the .nbd directory are exposed as raw block devices, so you can put a conventional filesystem or a database on top of object storage:

sudo mkdir -p /mnt/zerofs/.nbd
sudo truncate -s 1G /mnt/zerofs/.nbd/volume1
ls -lh /mnt/zerofs/.nbd/
sudo rm -f /mnt/zerofs/.nbd/volume1

The NBD server listens on 127.0.0.1:10809. Attach the device from the same virtual machine, or reach it from elsewhere over an SSH tunnel as in Step 10.

Step 10: Reach the filesystem from another host

The NFS, 9P and NBD servers are unauthenticated protocols and are deliberately bound to loopback, so they are not reachable across the network. To mount the filesystem from another machine, forward the port over SSH from your client and mount through the tunnel. Run these on the client, not on the appliance:

$ ssh -i ~/.ssh/id_rsa -N -L 5564:127.0.0.1:5564 azureuser@<your VM address>

$ sudo mount -t 9p -o trans=tcp,port=5564,version=9p2000.L,cache=mmap,access=user 127.0.0.1 /mnt/zerofs

If you would rather expose a protocol port directly on a private network, bind it to the VM's private address in /etc/zerofs/config.toml and restrict it with a Network Security Group rule to the specific source range that needs it. Never expose these ports to the public internet: they carry no authentication of their own.

Step 11: Point ZeroFS at your own storage

The bundled object store makes the appliance work immediately, but the reason to run ZeroFS is usually your own object storage. Repointing is a configuration change and a restart. Doing so starts a brand new, empty filesystem — the data already in the bundled store is not migrated, so copy anything you want to keep off /mnt/zerofs first.

Azure Blob Storage

Edit the [storage] section of /etc/zerofs/config.toml and replace the [aws] block with an [azure] block:

[storage]
url = "azure://<your-container>/zerofs-data"
encryption_password = "${ZEROFS_PASSWORD}"

[azure]
storage_account_name = "${AZURE_STORAGE_ACCOUNT_NAME}"
storage_account_key = "${AZURE_STORAGE_ACCOUNT_KEY}"

Then add the account credentials to /etc/zerofs/zerofs.env, keeping the existing ZEROFS_PASSWORD line unchanged, and restart:

$ sudo tee -a /etc/zerofs/zerofs.env <<'EOF'
AZURE_STORAGE_ACCOUNT_NAME=<your-storage-account>
AZURE_STORAGE_ACCOUNT_KEY=<your-storage-account-key>
EOF

$ sudo systemctl restart zerofs zerofs-mount
$ sudo systemctl status zerofs --no-pager

Consider granting the virtual machine a managed identity and using it in place of a stored account key.

Amazon S3 or any S3 compatible store

[storage]
url = "s3://<your-bucket>/zerofs-data"
encryption_password = "${ZEROFS_PASSWORD}"

[aws]
access_key_id = "${AWS_ACCESS_KEY_ID}"
secret_access_key = "${AWS_SECRET_ACCESS_KEY}"
default_region = "us-east-1"

Amazon S3 implements conditional writes natively, which is what ZeroFS needs for fencing. On real S3 you can therefore remove the conditional_put line from the [aws] block and stop the local coordinator:

$ sudo systemctl disable --now redis-server

Keep conditional_put pointing at Redis for any S3 compatible store that does not correctly implement conditional writes. ZeroFS checks this at start up and refuses to run against a store that would silently lose the guarantee, so if it starts, the guarantee holds.

Choose a hot storage class

Whatever backend you use, keep it on a hot, standard access tier. Archive tiers make the filesystem unusable, and infrequent access tiers charge retrieval on the constant reads ZeroFS performs, which usually costs more rather than less.

Step 12: Verify the deployment

Confirm every service is running:

systemctl is-active zerofs.service zerofs-mount.service garage.service redis-server.service nginx.service

Expected output:

active
active
active
active
active

systemctl reporting all five appliance services active, with a note explaining that zerofs provides the filesystem, garage the bundled object store, redis-server the conditional write coordinator and nginx the TLS and authentication front

Confirm the network exposure. Only 22, 80 and 443 face the network; everything else is on loopback:

ss -tlnH | awk '{print $4}' | sort -u

Expected output:

0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
127.0.0.1:10809
127.0.0.1:2049
127.0.0.1:3901
127.0.0.1:3910
127.0.0.1:3913
127.0.0.1:5564
127.0.0.1:6379
127.0.0.1:7000
127.0.0.1:8080
127.0.0.53%lo:53
127.0.0.54:53
[::1]:6379
[::]:22
[::]:443
[::]:80

Only 22, 80 and 443 are bound to a routable address. Everything ZeroFS serves — the Web UI on 8080, 9P on 5564, NFS on 2049, NBD on 10809 and the admin RPC on 7000 — is on loopback, as are the bundled object store (3901, 3910, 3913), Redis (6379) and the local DNS stub (53).

Run the built in self test, which proves the whole appliance end to end:

sudo /usr/local/bin/zerofs-selftest

Expected output:

OK zerofs self-test passed: healthz 200, unauth / and /ws/9p 401, authed / 200 (ZeroFS Web UI), authed /ws/9p 101, ZeroFS + object store + Redis all loopback-only, Redis requires auth, filesystem write/read/delete round-trip OK

The zerofs-selftest output reporting the health endpoint returning 200, unauthenticated requests to the Web UI and to the 9P WebSocket both refused with 401, and the authenticated round trip succeeding

Prove a real round trip through the filesystem, writing a megabyte and reading it back:

sudo bash -c 'head -c 1048576 /dev/urandom > /tmp/rt.bin; cp /tmp/rt.bin /mnt/zerofs/rt.bin; sync; \
  A=$(sha256sum /tmp/rt.bin | cut -d" " -f1); B=$(sha256sum /mnt/zerofs/rt.bin | cut -d" " -f1); \
  [ "$A" = "$B" ] && echo "ROUND-TRIP OK $A" || echo "MISMATCH"; rm -f /mnt/zerofs/rt.bin /tmp/rt.bin'

A one megabyte file written through the ZeroFS mount and listed alongside the seeded files, with the mount options, the matching checksum and the free space on the dedicated data volume

Step 13: TLS, redirect and the authentication gate

The health endpoint is deliberately unauthenticated so load balancer probes work:

curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1/healthz

Expected output:

200

Port 80 redirects to HTTPS:

curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/

Expected output:

301

Everything else is refused without the per instance credential, including the WebSocket that backs the file manager and the in browser terminal:

curl -sk -o /dev/null -w 'web ui: %{http_code}\n' https://127.0.0.1/
curl -sk -o /dev/null -w '9p websocket: %{http_code}\n' https://127.0.0.1/ws/9p

Expected output:

web ui: 401
9p websocket: 401

With the credential, the same request succeeds:

sudo bash -c 'U=$(grep -m1 "^ZEROFS_UI_USER=" /root/zerofs-credentials.txt | cut -d= -f2-); \
  P=$(grep -m1 "^ZEROFS_UI_PASSWORD=" /root/zerofs-credentials.txt | cut -d= -f2-); \
  curl -sk -o /dev/null -w "authenticated: %{http_code}\n" -u "$U:$P" https://127.0.0.1/'

Expected output:

authenticated: 200

Step 14: Replace the certificate for production

The image generates a self signed certificate for this virtual machine on first boot, with the VM address in its Subject Alternative Names. For production, put your own certificate in place and reload nginx:

$ sudo cp /path/to/fullchain.pem /etc/nginx/tls/zerofs.crt
$ sudo cp /path/to/privkey.pem   /etc/nginx/tls/zerofs.key
$ sudo chmod 0600 /etc/nginx/tls/zerofs.key
$ sudo nginx -t && sudo systemctl reload nginx

To change the web password, rewrite the basic authentication file with the bundled helper. Only the bcrypt hash is stored:

$ sudo /usr/local/sbin/zerofs-write-htpasswd admin '<a strong new password>'
$ sudo systemctl reload nginx

Remember to update /root/zerofs-credentials.txt so the recorded value stays accurate.

Step 15: Baked version and configuration

zerofs --version
dpkg-query -W -f='zerofs deb version: ${Version}\n' zerofs

Expected output:

zerofs 2.3.2
zerofs deb version: 2.3.2

Key paths on the appliance:

Path Purpose
/etc/zerofs/config.toml ZeroFS configuration: storage backend, cache and server ports
/etc/zerofs/zerofs.env Per VM secrets, root only: encryption password, object store key, Redis password
/root/zerofs-credentials.txt Per VM web credential and URL, root only
/mnt/zerofs The ZeroFS filesystem, mounted on the VM
/srv/zerofs The dedicated data volume holding the object store and the cache
/etc/nginx/sites-available/cloudimg-zerofs The TLS and authentication front
/usr/local/bin/zerofs-selftest End to end appliance self test

The image ships ZeroFS 2.3.2 from the project's own repository. Ubuntu's unattended upgrades act only on Ubuntu's own security origins, so ZeroFS is never upgraded automatically underneath you. Take a newer release deliberately when you are ready:

$ sudo apt-get update && sudo apt-get install --only-upgrade zerofs
$ sudo systemctl restart zerofs zerofs-mount

Security notes

  • The Web UI has no authentication of its own. Access is controlled entirely by the nginx basic authentication credential over TLS. Its WebSocket endpoint is a full read and write session on the filesystem, its gRPC endpoint exposes administrative methods and accepts any origin, and its terminal boots a Linux VM with the filesystem mounted. Never bind ZeroFS to a non loopback address, and never remove the auth_basic directives.

  • NFS, 9P and NBD carry no authentication. They are bound to loopback on this image. If you expose one on a private network, restrict it with a Network Security Group rule to the exact source range that needs it.

  • Every secret is per virtual machine. The encryption password, web password, object store keys, Redis password and TLS certificate are all generated on first boot. Nothing usable is baked into the image, and ZeroFS and the object store are both prevented from starting until first boot has produced them.

  • The encryption password is not recoverable. It lives only on your virtual machine in /etc/zerofs/zerofs.env. Back it up before storing anything important.

  • Restrict SSH. Allow port 22 only from your management network, and keep key based authentication.

  • The base image ships fully patched with unattended security upgrades enabled.

Support

cloudimg provides 24/7 support with a guaranteed 24 hour response SLA for this image. ZeroFS itself is open source under the GNU Affero General Public License v3.0 and is shipped unmodified; cloudimg is not affiliated with the ZeroFS project. For questions about the image, its configuration or its security posture, contact cloudimg support.