Storage Azure

JuiceFS on Ubuntu 24.04 on Azure User Guide

| Product: JuiceFS on Ubuntu 24.04 LTS on Azure

Overview

This image runs JuiceFS Community Edition 1.4.0, fully installed and configured on Ubuntu 24.04 LTS as a working single node POSIX filesystem. JuiceFS is a distributed filesystem that separates metadata from data: file names, directory structure, permissions and the chunk layout live in a metadata engine, while the file contents themselves are stored as immutable objects in an object store. That separation is what lets JuiceFS present a normal POSIX mount point backed by object storage, with full support for directories, symlinks, extended attributes and random writes.

On this image both halves are provisioned for you and both live on a dedicated data disk:

  • Metadata engine — PostgreSQL 16, bound to loopback only, with its cluster on the data disk.
  • Object store — a local directory on the same data disk.
  • Mount — the volume is mounted at /mnt/juicefs by a systemd unit, juicefs-mount.service.

Within a few minutes of launch you have a working filesystem at /mnt/juicefs that you can use exactly like any other path.

Why PostgreSQL as the metadata engine. JuiceFS supports several metadata engines. This image deliberately uses PostgreSQL: it is transactional and durable, it supports concurrent clients, and it is straightforward to back up with standard tooling. The single file engines JuiceFS also offers are limited to one process at a time, which would make a concurrent mount impossible.

Boot safety. The volume is mounted by a systemd unit and deliberately not from /etc/fstab. A FUSE entry in /etc/fstab that cannot mount will block local-fs.target and can leave a virtual machine unbootable. With a systemd unit, a mount problem degrades gracefully: the machine still boots, SSH still works, and systemctl status juicefs-mount explains what went wrong.

Secure by default — no known bootstrap credential and no shared filesystem identity. Nothing is formatted when this image is built. On the first boot of your instance a one shot service generates a unique metadata engine password and formats a fresh volume with its own UUID for that specific virtual machine, then writes the details to a root owned file at /root/juicefs-credentials.txt. No two instances share a metadata credential, a volume UUID or an object namespace. The PostgreSQL role that owns the metadata ships with no password and no login rights until first boot sets them.

What is included:

  • JuiceFS Community Edition 1.4.0, the official upstream release, verified against the published checksum manifest, installed at /usr/local/bin/juicefs.
  • PostgreSQL 16 as the metadata engine, bound to 127.0.0.1 only, with its data directory on the dedicated data disk.
  • A local object store on the same data disk, and a read cache.
  • juicefs-mount.service and juicefs-firstboot.service under systemd, plus a shipped end to end self test at /usr/local/sbin/juicefs-selftest.
  • The upstream Apache 2.0 licence notice at /usr/share/doc/juicefs/LICENSE.
  • A fully patched base with unattended security upgrades enabled, paired with this deployment guide and 24/7 cloudimg support.

Prerequisites

  • The virtual machine deployed from this image, with SSH access as azureuser.
  • Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) to start. JuiceFS benefits from more memory for its cache, so scale up for heavier workloads.
  • No inbound network ports are required. This is a single node filesystem appliance: the metadata engine is bound to loopback and the volume is consumed locally at /mnt/juicefs. You do not need to open any port in the network security group to use it.
  • The dedicated data disk is attached, formatted and mounted at /var/lib/juicefs automatically. Both the metadata engine and the object store live there.

Verify the filesystem is running

SSH into the virtual machine as azureuser and confirm JuiceFS is installed, the mount unit is active, and the volume is mounted.

juicefs version
sudo systemctl is-active juicefs-mount.service
mountpoint /mnt/juicefs
df -h /mnt/juicefs | tail -2

You should see the JuiceFS version, active, confirmation that /mnt/juicefs is a mountpoint, and a df line showing the filesystem as JuiceFS:cloudimg. The reported size is a very large logical figure because JuiceFS presents an object store backed namespace rather than a fixed block device; actual usable capacity is governed by the data disk, which you can check with df -h /var/lib/juicefs.

The juicefs version command reporting JuiceFS 1.4.0, systemctl reporting the juicefs mount service active, confirmation that slash mnt slash juicefs is a mountpoint, and a df listing showing the JuiceFS colon cloudimg filesystem mounted at slash mnt slash juicefs

Retrieve the per instance credentials

The metadata engine password and the volume identity for this specific virtual machine were generated on first boot and written to a root owned file. Read them with sudo.

sudo grep -E '^juicefs\.' /root/juicefs-credentials.txt

The file records the volume name, the volume UUID, the mount point, the metadata engine type and URL, the metadata password, and the object store location. These values are unique to this instance and are not baked into the image. Keep the file secret — the metadata password is what grants access to the index of your filesystem. The screenshot below has the password masked.

The per instance JuiceFS credentials file showing the volume name cloudimg, a unique volume UUID, the mount point slash mnt slash juicefs, the postgresql metadata engine and its loopback URL, the metadata password masked, and the local object store path, all generated on first boot

The same metadata password is also written to /etc/juicefs/meta.env, a root owned file with mode 0600, which is where juicefs-mount.service reads it from at boot. You do not need to supply it for normal filesystem use — only for juicefs administrative subcommands that talk to the metadata engine directly.

Use the filesystem

/mnt/juicefs is a normal POSIX path. Create directories, copy files into it and read them back exactly as you would on any other filesystem.

sudo mkdir -p /mnt/juicefs/projects
echo "hello from cloudimg JuiceFS" | sudo tee /mnt/juicefs/projects/note.txt
sudo dd if=/dev/urandom of=/mnt/juicefs/projects/data.bin bs=1M count=16 status=none
sync
sudo sha256sum /mnt/juicefs/projects/data.bin
ls -lh /mnt/juicefs/projects

You should see the checksum of the 16 MiB file you just wrote, and a directory listing showing both files. JuiceFS supports the full range of POSIX behaviour, including symlinks and extended attributes:

sudo ln -sf note.txt /mnt/juicefs/projects/note.link
sudo setfattr -n user.project -v cloudimg /mnt/juicefs/projects/note.txt
sudo getfattr -n user.project --only-values /mnt/juicefs/projects/note.txt; echo
ls -l /mnt/juicefs/projects/note.link

The extended attribute reads back as cloudimg and the symlink resolves to note.txt. The volume is mounted with --enable-xattr, so extended attributes are stored in the metadata engine alongside the rest of the file metadata.

A directory created on the JuiceFS volume with a text file and a 16 MiB random binary file written into it, the sha256 checksum of that binary, a directory listing showing both files, and a symlink plus an extended attribute set and read back successfully to prove full POSIX semantics

Confirm the metadata really lives in the metadata engine

This is what distinguishes a JuiceFS volume from an ordinary directory. juicefs info reads a file's inode and its chunk and slice layout straight out of the metadata engine.

sudo juicefs info /mnt/juicefs/projects/data.bin | sed -n '1,9p'

You should see the file's inode number, its length and size, and its logical path inside the volume. The objects table below that lists the individual immutable objects in the object store that hold the file's contents — a 16 MiB file is split across several 4 MiB objects.

You can also confirm the metadata engine genuinely holds the filesystem index by counting its tables directly in PostgreSQL:

sudo -u postgres psql -qtA -d juicefs \
  -c "SELECT count(*) FROM information_schema.tables WHERE table_schema='public' AND table_name LIKE 'jfs%'"

This returns the number of JuiceFS metadata tables in the database. A non zero count confirms that the volume's index — inodes, directory entries, chunk maps, extended attributes — is stored transactionally in PostgreSQL, not merely cached in memory.

Verify the appliance end to end

The image ships a self test that proves the filesystem genuinely works rather than merely checking that a process is running. Run it at any time:

sudo /usr/local/sbin/juicefs-selftest

The self test writes a known dataset through the POSIX mount, creates a symlink and an extended attribute, resolves the file's inode and cross checks that the inode is present in the PostgreSQL metadata engine, performs a full unmount and remount with the cache dropped so the read back is served from the object store rather than a warm cache, verifies every file is byte identical by sha256, runs juicefs fsck, and then runs two negative controls: a wrong metadata password must be refused, and a file whose backing object has been deleted must fail to read rather than silently returning success. It finishes with SELFTEST_SUCCESS.

The shipped JuiceFS self test running to completion and reporting selftest OK, confirming POSIX write, symlink and extended attribute support, metadata proven in PostgreSQL by inode, survival of an unmount and remount with a cold cache, three files byte identical by sha256, a clean fsck, a wrong password refused, a deleted object read refused, and the metadata engine reachable on loopback only

Inspect the volume

juicefs status reports the volume's settings and its active client sessions. It talks to the metadata engine directly, so it needs the metadata password from the credentials file.

export META_PASSWORD=$(sudo grep '^juicefs.meta.password=' /root/juicefs-credentials.txt | cut -d= -f2-)
META_URL=$(sudo grep '^juicefs.meta.url=' /root/juicefs-credentials.txt | cut -d= -f2-)
sudo META_PASSWORD="$META_PASSWORD" juicefs status "$META_URL" | sed -n '1,20p'

The output shows the volume Name, its UUID, the storage type, the bucket path, the block size and the trash retention, followed by the list of active sessions. To see how much space the volume is actually consuming on the data disk:

sudo du -sh /var/lib/juicefs/data
df -h /var/lib/juicefs | tail -1

Protecting the metadata engine

JuiceFS splits metadata from data, and this has an important operational consequence: the metadata engine is the index of your filesystem. Every byte of data can survive intact in the object store and the volume is still unusable without its metadata. Treat the PostgreSQL database as the thing to protect.

JuiceFS can export a consistent metadata dump at any time:

export META_PASSWORD=$(sudo grep '^juicefs.meta.password=' /root/juicefs-credentials.txt | cut -d= -f2-)
META_URL=$(sudo grep '^juicefs.meta.url=' /root/juicefs-credentials.txt | cut -d= -f2-)
sudo mkdir -p /var/backups/juicefs
sudo META_PASSWORD="$META_PASSWORD" juicefs dump "$META_URL" /var/backups/juicefs/meta-dump.json
sudo ls -lh /var/backups/juicefs/meta-dump.json

This writes a portable JSON dump of the entire filesystem index, which juicefs load can restore into a metadata engine. JuiceFS also takes automatic periodic metadata backups into the object store under meta/ by default.

For a belt and braces backup, take a standard PostgreSQL dump as well:

sudo -u postgres pg_dump -Fc juicefs -f /var/backups/juicefs/juicefs-meta.dump
sudo ls -lh /var/backups/juicefs/juicefs-meta.dump

Copy both files, plus a record of the metadata password from /root/juicefs-credentials.txt, somewhere off the instance — for example Azure Blob Storage. Schedule them with a cron job or systemd timer to suit your recovery objectives.

Where your data lives

Understanding the layout makes capacity planning straightforward:

Path Contents
/mnt/juicefs The POSIX mount point your applications use.
/var/lib/juicefs The dedicated data disk. Everything durable lives here.
/var/lib/juicefs/data The object store — the immutable objects holding file contents.
/var/lib/juicefs/pgdata The PostgreSQL cluster holding the metadata engine.
/var/lib/juicefs/cache The local read cache. Safe to clear; it is rebuilt on demand.

Because both the object store and the metadata engine live on the dedicated data disk, usable capacity is the size of that disk. To grow the filesystem, expand the data disk in the Azure portal and then grow the filesystem on it — no JuiceFS reconfiguration is needed, because JuiceFS simply keeps writing objects into the same directory.

Managing the mount

The volume is mounted by a systemd unit, so the usual systemd commands apply:

sudo systemctl status juicefs-mount.service --no-pager | sed -n '1,12p'

To unmount and remount the volume, stop and start that unit rather than calling umount directly, so systemd's view stays consistent:

sudo systemctl restart juicefs-mount.service
sleep 5
mountpoint /mnt/juicefs

If the mount ever fails to come up, sudo journalctl -u juicefs-mount.service will show why. The most common causes are the metadata engine not being ready yet, or /etc/juicefs/meta.env being missing — the mount unit will not start without it, which is what stops a fresh machine from crash looping before first boot has run.

Licensing

JuiceFS Community Edition is distributed under the Apache License 2.0. The upstream licence text that governs the exact binary shipped in this image is installed at /usr/share/doc/juicefs/LICENSE:

head -3 /usr/share/doc/juicefs/LICENSE

This image ships the Community Edition. cloudimg provides the image, the deployment guide and support for the image itself; it is not affiliated with or endorsed by the JuiceFS project.

Support

cloudimg provides 24/7 support for this image. If the filesystem does not behave as this guide describes, start with the shipped self test — sudo /usr/local/sbin/juicefs-selftest — and the mount unit's journal, and include their output with any support request.