JuiceFS
JuiceFS puts a real POSIX filesystem on top of object
storage: file data is split into chunks and stored in an S3 bucket, while a
separate metadata engine (SQLite, Redis, PostgreSQL…) keeps the namespace,
file attributes and chunk maps. You get an ordinary directory you can ls,
cat and grep — every write eventually lands as immutable objects in the
bucket. It solves the “I want a filesystem, but the bytes should live in S3”
problem: snapshots are cheap (objects are immutable), capacity is the bucket’s,
and the same bucket can be re-mounted from any host that knows the metadata
URL.
As a pgcli addon, JuiceFS is pgcli’s first host-binary addon: there is no
container. pg addon install juicefs downloads the official binary from GitHub
releases (sha256-verified; a juicefs already on PATH is reused), formats
the volume against an external S3 service and mounts it on the host.
Platform: Linux only (macOS refuses with a clear error, like BeeGFS). The mount is a plain host directory — visible to host processes only. It does NOT propagate into running PG instance containers (no rshared mount propagation in v1; the addon manages this host’s mount, not a cluster’s).
Everything is explicit
Both sides of the volume are named on the command line — pgcli never auto-detects or inherits from same-host addons:
- Object store: an EXTERNAL S3 service (cloud, or a MinIO on another
machine) given as
--s3-endpoint http(s)://host[:port]plus--access-key/--secret-key. pgcli probes the endpoint before formatting, so an unreachable host fails fast with “not answering” instead of hanging insidejuicefs format. The endpoint is baked into the volume metadata at format time — later mounts need only the metadata URL. - Metadata engine: an explicit URL —
--dsn(alias--meta-dsn) acceptingpostgres://,redis://orsqlite3://. pgcli never creates or drops the database; for PostgreSQL it must already exist. - Bucket: never created by pgcli. Provision it yourself (once) with the
pg mcwrapper — the install prints the exact two lines:
Any engine works the same way — pick the URL:
The mount point is yours to choose — by default it lives under pgcli’s addon
dir; point it anywhere with --mount-point. Create the directory yourself
(mkdir -p) at a path the user running pg has read-write access to:
The three engines can all be pgcli’s own services — created with pgcli, then wired in explicitly as ordinary external URLs (the install never looks them up by itself; it could equally well point at a managed DB or a cloud bucket):
These examples run pg on the same machine as the store, so loopback
(default 127.0.0.1) is enough. When the JuiceFS client lives on another
host, publish the store on the network and point --s3-endpoint at its
address — the endpoint is baked into the volume at format time, so publish it
from the start:
(The same note applies to the redis example: redis already binds 0.0.0.0
by default — its password is the gate.)
The same volume can then be mounted from any host that has the metadata URL — the credentials and endpoint already live inside it.
Parameters
| Flag | Default | Description |
|---|---|---|
--name |
juicefs |
Volume name (config map key; also the JuiceFS volume name at format time) |
--dsn / --meta-dsn |
— | REQUIRED. Metadata-engine URL: postgres://, redis:// or sqlite3:// |
--s3-endpoint |
— | REQUIRED. External S3 service as http(s)://host[:port] (no bucket path, no embedded credentials) |
--access-key / --secret-key |
— | REQUIRED together. S3 credentials for the external service |
--bucket |
juicefs-<name> |
Bucket name — pgcli does NOT create it (see above) |
--mount-point |
<base>/addon/juicefs/<name>/mnt |
Host directory to mount — pgcli never creates it: mkdir -p it beforehand, writable for the user running pg |
--cache-dir |
<base>/addon/juicefs/<name>/cache |
Local data cache dir |
--install-dir |
/usr/local/bin |
Where the host binary goes (falls back to ~/.local/bin when not writable — keep it on PATH) |
--version |
pinned (1.4.1) |
Release to download (its sha256 is pinned in pgcli; pass a version whose digest pgcli knows) |
--no-mount |
off | Format and configure, leave it unmounted |
--force |
off | Rebuild the config entry from the flags (format is still skipped when the volume already exists in the metadata engine) |
FUSE itself (/dev/fuse + fusermount3) is probed, never installed — a
missing fuse3 package fails the install with the apt line to run.
Daily operations
remove never deletes S3 data or the metadata store — losing the --dsn
target means losing the filesystem. The mount owner is the user running pg.
Moving to a different mount point
The mount point is recorded on the config entry, and mount accepts a
--mount-point override that relocates the volume — the new path is persisted
to pg.yaml, so later plain mounts (and autostart) use it. The old mount
must be down first, and the new directory must exist and be writable (same
contract as install):
Attempting the move while still mounted refuses with an “umount first” hint;
pointing it at a missing directory fails with the mkdir -p remedy. The mount
point is fully decoupled from pgcli’s bookkeeping (pid/log/cache stay under
<base>/addon/juicefs/<name>/), so relocation never disturbs the volume
itself — it is the same bucket and metadata re-attached elsewhere.
Autostart
JuiceFS is a host-binary addon, so pg start --autostart re-mounts the
volume at boot instead of starting a container. The backend is external — boot
succeeds only when the S3 endpoint is reachable again. It never re-formats. The
mount comes up as the user the boot service runs as.