Updated: 2026-07-12

After living with JuiceFS on a three-node cluster for a while, I think it’s worth writing the install walkthrough I would have liked to find when starting. There’s good official documentation, but it’s fragmented across very different use cases, and someone arriving from NFS or from a classic bind mount gets lost on the first real decision: which metadata backend to pick and how to mount the thing without surprises.

This walkthrough assumes a specific scenario: three modern Linux servers that need to see the same files, a PostgreSQL database already available, and S3-compatible storage as the final destination for the data. It is the most common pattern in small and medium deployments.

Key takeaways

  • JuiceFS delegates data to an object store (S3, MinIO, Hetzner) and metadata to a database you already operate (Redis, PostgreSQL, MySQL).

  • From the client side it’s a POSIX FUSE filesystem with configurable local cache: mount it and use it like any other directory.

  • The object store bucket must be exclusive to JuiceFS: it uses an internal key structure that assumes exclusivity.

  • The --free-space-ratio 0.3 option is important and often forgotten: without it, a spike can fill the cache disk.

  • For simple single-network cases, NFS remains valid. JuiceFS pays off when you want replication or node-failure resilience.

Why JuiceFS and not something else

The classic alternative is NFS. It works, it’s well-known, and it’s supported by every Linux kernel, but it comes with a well-documented pile of problems: fragile cache semantics, difficulty scaling reads, and a network footprint admins usually have to wrap in specific firewalls. The original design of NFS[1] (NFSv2) was deliberately stateless; it wasn’t until NFSv4 that the protocol became stateful, a shift that carries much of that cache fragility along with it. When a node drops, the client experience ranges from a long stall to log noise that is hard to diagnose.

CephFS solves the problem at scale: a cluster of Metadata Servers (MDS) spreads the metadata, and clients talk directly to RADOS, Ceph’s distributed object store, to read and write blocks, per the official CephFS documentation[2]. But installing Ceph to share a few terabytes across three machines is overkill. Running a Ceph cluster in production has a notable learning curve and demands real dedication.

JuiceFS sits in the middle. It delegates data to an object store and metadata to a database you already know how to operate. From the client side it is a FUSE system that behaves like a normal POSIX filesystem, with configurable local cache. The elegant part is that all the distributed-systems complexity is absorbed by components you already know how to monitor. The JuiceFS GitHub repository[3] itself documents throughput up to ten times higher than EFS or S3FS in sequential tests, and it passes 8813 pjdfstest POSIX-compatibility tests. This same principle of reusing existing infrastructure is what we cover in Vector for observability.

Preparing the backend

Before touching JuiceFS, two pieces should be in place.

The object store: you need an endpoint, credentials, and the bucket name. If you use AWS S3, self-hosted MinIO, or any compatible service, the setup is equivalent. The bucket must not be shared with other systems: JuiceFS uses an internal key structure that assumes exclusivity.

The metadata database: I recommend PostgreSQL if you already have an instance. JuiceFS creates its tables in a dedicated schema, the load is moderate, and the ops side (backups, monitoring, high availability) benefits from all the infrastructure you already have for PG. The official JuiceFS documentation on metadata engines[4] puts the cost of a relational engine like PostgreSQL at around 600 bytes per file, a useful figure for sizing the database disk before you start. Redis is faster for very intensive operations, but for typical workloads the difference does not justify adding a new component.

Create a dedicated database and a user with permissions on it.

Initial formatting

With the backend ready, format the volume. You do it once from any node:

juicefs format 
  --storage s3 
  --bucket https://endpoint/bucket 
  --access-key <KEY> 
  --secret-key <SECRET> 
  --compress lz4 
  postgres://user:password@host:5432/juicefs my-volume

Two options deserve attention:

  • lz4 compression is a good default: adds a small CPU cost and reduces the volume sent to the object store by 20-40%.

  • The volume name is internal JuiceFS metadata and appears in metrics and logs.

Don’t format with --trash-days 0 in production. JuiceFS’s internal trash, enabled by default since version 1.0.0 per the trash documentation[5], has saved more than one accidental deletion; cleanup runs every hour by default, so don’t expect freed space to show up instantly. JuiceFS’s trash solves a narrow problem; it is not a substitute for a real backup, which is why I still rely on restic pointed at the same S3 bucket.

Mounting on each node

Once formatted, each node mounts the filesystem with a single command:

juicefs mount 
  --cache-dir /var/juicefs-cache 
  --cache-size 20480 
  --free-space-ratio 0.3 
  postgres://user:password@host:5432/juicefs 
  /mnt/jfs

Cache options are where you’ll have the most variation between nodes.

  • The size is expressed in MiB; 20480 is 20 GiB. If a node has a fast SSD and spare space, it is worth raising this value.

  • --free-space-ratio 0.3 is important and often forgotten. It tells JuiceFS that once the disk hosting the cache drops below 30% free, it should start evicting entries. Without this, a burst of activity can fill the disk and cause problems for the system.

For a persistent mount across reboots, the clean approach is a systemd unit that runs the same command with ExecStart. That gives you more control over dependencies (waiting for the network, for PostgreSQL, and so on) than a plain fstab entry.

Verification and first tests

Before considering it operational, three tests are worth running:

  • Create a file on one node and read it from another: with correct configuration, it should appear within seconds. If it takes much longer, there is probably a network problem with the object store or the metadata database.

  • Write a large file (several GB) from one node while monitoring CPU and network. For large workloads, JuiceFS uses multiple parallel connections to the object store, and throughput should get close to the available bandwidth.

  • Power off the node that wrote the file and check that the others keep reading it without trouble. This is the test that separates a real shared filesystem from a mirage: the data lives in the object store, not on the writing node’s local disk.

Maintenance and monitoring

Once running, what needs watching is less the filesystem itself than its dependencies:

  • The metadata database needs normal PostgreSQL monitoring: transaction duration, active connections, table size.

  • The object store needs cost and error-rate monitoring.

  • JuiceFS exposes Prometheus metrics at http://localhost:9567/metrics per the monitoring documentation[6], a local endpoint worth scraping. If you have not yet figured out how to alert on those metrics without them getting ignored, I already wrote about writing Prometheus alerts that actually get handled.

The trickiest operation you’ll eventually need is cleaning up orphan blocks (juicefs gc). JuiceFS is conservative by design: when you delete a file, it marks its blocks as reclaimable but does not delete them from the object store until the collector cleans them up. Running gc weekly with --compact is good hygiene, especially if your workload involves lots of deletes.

If you ever need to migrate to a different object store, JuiceFS has a sync command that parallelizes the copy without stopping the service. With NFS, that operation would be a major undertaking.

Frequently asked questions

Can I use JuiceFS without an S3-compatible object store?

Not in any practical way. JuiceFS needs a block-data backend, and S3 (or an S3-compatible one: MinIO, Hetzner Object Storage, Backblaze B2) is the standard choice. It also supports other backends, such as the local filesystem or HDFS, but you lose the distributed advantage that makes JuiceFS worth choosing in the first place.

What happens if the metadata database goes down?

The filesystem stops responding until the database comes back. Data in the object store is not lost, but no client can read or write while metadata is unavailable. That is why PostgreSQL high availability, with replicas and failover, matters as much as JuiceFS’s own resilience.

Does JuiceFS handle large numbers of small files well?

With caveats. The per-file cost in the metadata database runs around 600 bytes, so millions of small files put more pressure on the database than on the object store. For workloads with tens of millions of files, size PostgreSQL accordingly from the start, not as an afterthought.

Conclusion

What makes JuiceFS a good choice isn’t any single feature, but its alignment with components you already operate. Every Linux team that runs Linux for real knows how to back up Postgres, monitor an S3 bucket, and read Prometheus metrics. JuiceFS turns that into a shared filesystem, and does so without requiring a new operational plane.

The honest alternative is still NFS for very simple cases where all clients are on the same network and failure risk isn’t critical. For anything a little more serious, or where you want cross-region replication without reconfiguring clients, this route is worth a try.

This article is also available in Spanish.

Sources

  1. NFS
  2. official CephFS documentation
  3. JuiceFS GitHub repository
  4. official JuiceFS documentation on metadata engines
  5. trash documentation
  6. monitoring documentation
  7. JuiceFS — official documentation: introduction