S3 client examples¶
Replace YOUR-ENDPOINT, YOUR_ACCESS_KEY, and YOUR_SECRET_KEY with the values from
your welcome email. Use path-style addressing (the examples below already do).
AWS CLI¶
# Configure a named profile
aws configure set aws_access_key_id YOUR_ACCESS_KEY --profile zendrive
aws configure set aws_secret_access_key YOUR_SECRET_KEY --profile zendrive
aws configure set region us-east-1 --profile zendrive
# Use it (read-only: list and download)
aws --profile zendrive --endpoint-url "https://YOUR-ENDPOINT/" s3 ls
aws --profile zendrive --endpoint-url "https://YOUR-ENDPOINT/" s3 cp s3://my-bucket/file.txt ./
rclone¶
# ~/.config/rclone/rclone.conf
[zendrive]
type = s3
provider = Other
access_key_id = YOUR_ACCESS_KEY
secret_access_key = YOUR_SECRET_KEY
endpoint = https://YOUR-ENDPOINT/
force_path_style = true
rclone lsd zendrive:
rclone copy --fast-list zendrive:my-bucket/ ./localdir
Use --fast-list for big libraries
By default rclone makes one API call per directory, which is slow on a
high-latency S3 endpoint. --fast-list reads the whole tree recursively —
one LIST call per ~1000 objects — so commands that walk the library
(lsf, ls, size, copy, sync, check) run far faster. It trades RAM
for speed (~1 KB per object, so ~500 MB for a 500k-file library); drop it
if you're memory-constrained. See slow scans.
Mount for streaming (rclone VFS)¶
To stream directly from the storage, mount it with minimal VFS cache mode.
Concurrent chunked reads keep playback smooth, while minimal streams data straight
through instead of pulling whole files to disk — which keeps your bandwidth tier under
control, especially during a full library scan. If you want disk-buffered
seeking/scrubbing and have bandwidth headroom, --vfs-cache-mode full is the
alternative — see the tip below.
rclone mount zendrive:my-bucket /mnt/zendrive \
--read-only \
--vfs-cache-mode minimal \
--vfs-read-chunk-size 4M \
--vfs-read-chunk-streams 16 \
--buffer-size 32M \
--dir-cache-time 1000h \
--poll-interval 0 \
--use-server-modtime \
--fast-list \
--rc --rc-addr 127.0.0.1:5572 --rc-no-auth \
--daemon
Use --use-server-modtime, never --no-modtime, with a media server
--no-modtime makes rclone report a single fixed timestamp (2000-01-01) for
every file. Emby, Plex, and Jellyfin then see identical dates, so
"Recently Added" and date sorting break, and the server can't tell which files
have actually changed. Worse, if you remove --no-modtime later, every real date
suddenly differs from the placeholder the server stored — triggering a
full re-probe of the entire library (a days-long ffprobe storm on a large
catalog). Use --use-server-modtime instead: it reports each file's real
modification time, which S3 already returns in the LIST response, so it costs
no extra per-file request — the same performance win as --no-modtime, but
with correct dates.
Why minimal cache mode — and when full is worth it
--vfs-cache-mode minimal streams reads straight from S3 and pulls only what the
player or scanner actually asks for. That matters most during a full library
scan: your media server reads just the header of each file, and heavier modes can
pull far more data than the metadata needs — saturating your bandwidth tier for the
length of the scan. full mode instead buffers whole reads to disk (add
--vfs-cache-max-size on SSD/NVMe, plus --vfs-read-ahead), which helps
seeking/scrubbing but can amplify scan bandwidth, so switch to it only if you have
headroom. The Saltbox scan profile below also uses minimal.
Once the mount is up, warm the directory cache so browsing is fast from the first click. The recommended approach — and what we run in production — is a light non-recursive warm of the top-level listings: one async refresh per top-level directory, which is easy on the backend because it never walks the deep tree.
for d in /mnt/zendrive/*/; do
rclone rc vfs/refresh dir="$(basename "$d")" recursive=false _async=true \
--rc-addr 127.0.0.1:5572
done
(Adjust /mnt/zendrive to your mount point. If you mounted the whole remote —
zendrive: instead of zendrive:my-bucket — the same loop works unchanged: the
top-level entries are then your buckets.) For a boot-time service that warms several
mounts safely — wait-for-RC, limited parallelism, per-directory timeouts — see
Warming the rclone cache at startup.
| Flag | Why |
|---|---|
--vfs-cache-mode minimal |
Streams reads straight from S3 without buffering whole files to disk — keeps bandwidth controlled, especially during library scans. Switch to --vfs-cache-mode full (with --vfs-cache-max-size on SSD/NVMe and --vfs-read-ahead) only if you want disk-buffered seeking and have bandwidth headroom. |
--vfs-read-chunk-streams 16 + --vfs-read-chunk-size 4M |
rclone's recommended starting point for S3 — downloads chunks concurrently for fast start and high throughput. |
--buffer-size 32M |
In-memory buffer per open file; keeps the TCP window full on high-latency links. |
--dir-cache-time 1000h + --poll-interval 0 |
S3 can't push change notifications, so don't poll — keep the directory listing cached and let live updates refresh it (next row). |
--use-server-modtime |
Reports each file's real modification time straight from the S3 LIST response — no extra per-file request, so it's as cheap as skipping modtimes but keeps dates correct for your media server. Do not use --no-modtime (see the warning above). |
--fast-list |
Used by direct rclone commands and the optional recursive deep prime — reads in batched LIST calls (one per ~1000 objects) instead of one call per directory. Costs ~1 KB RAM per object; drop it if memory-constrained. |
--read-only |
ZenDRIVE S3 Access is read-only; this prevents the mount from attempting writes. |
Pair the mount with live updates
The --rc --rc-addr 127.0.0.1:5572 flags expose rclone's remote-control API so
ZenLocalPoller can refresh the VFS cache the instant
content is added or removed — which is why the long --dir-cache-time above is
safe. 5572 is rclone's default RC port; set the poller's rclone.port to the
same value so both sides match (see the
config reference).
Why warming the cache fixes slow browsing
Browsing a live mount lists directories lazily, one at a time, and that path
ignores --fast-list — which is why the first browse after a reboot crawls.
Warming the top-level listings up front means the directories are already cached
when your media server asks for them. ZenLocalPoller keeps that cache fresh as
content changes, so you only warm once per boot.
Run it per directory, and from ExecStartPost for systemd mounts
Refreshes are serial — LIST pages chain one round trip after another — so
running one async job per top-level directory overlaps the waits and finishes
faster than a single whole-tree pass. For systemd mounts, put the loop in
ExecStartPost=:
ExecStartPost=/bin/sh -c 'sleep 5 && for d in /mnt/zendrive/*/; do \
rclone rc vfs/refresh dir="$$(basename "$$d")" recursive=false _async=true \
--rc-addr 127.0.0.1:5572; done'
(The doubled $$ is required: systemd expands single $ itself before the
shell ever runs.)
Optional: a full deep prime
The top-level warm makes browsing fast but does not pre-cache the deeper folders
a full library scan walks into. If you want a complete first scan to read entirely
from cache, run a recursive refresh once — the same loop with recursive=true,
which reads the whole tree in batched LIST calls (it honors --fast-list). On a
library the size of ZenDRIVE that is a heavy burst, so run it once and off-peak
rather than on every boot.
Needs a recent rclone
--vfs-read-chunk-streams requires rclone v1.69 or newer. On older builds,
drop that flag and rely on --vfs-read-chunk-size alone.
Using Saltbox?¶
Saltbox manages rclone mounts with ansible: it renders
saltbox_managed_rclone_<remote>.service from a Jinja2 template every time you run
sb install mounts.
Don't edit the unit file directly
Any change you make to /etc/systemd/system/saltbox_managed_rclone_*.service
is silently overwritten the next time Saltbox's mounts tag runs. The
durable place for mount settings is the template + settings.yml, below.
Saltbox ships no S3-tuned mount template (its stock ones target Google Drive, Dropbox, SFTP and OneDrive), so use a custom template:
1. Create the rclone remote ([zendrive]) in your rclone config exactly as shown
above — Saltbox doesn't create S3 remotes for you.
2. Save the ClearStreamer scan profile as
/opt/mount-templates/custom/generic-scan.j2:
Full template — /opt/mount-templates/custom/generic-scan.j2
# /etc/systemd/system/{{ _service_file }}
# ClearStreamer S3 scan profile (optimized for fast scans + streaming)
[Unit]
Description=Rclone ClearStreamer Scan Profile
After=network-online.target
[Service]
User={{ user.name }}
Group={{ user.name }}
Type=notify
ExecStartPre=/bin/mkdir -p "/mnt/remote/{{ rclone_remote_name }}"
ExecStart=/usr/bin/rclone mount \
--allow-other \
{% if (rclone_vfs_cache_dir_lookup | length > 0) and item.settings.vfs_cache.enabled %}
--cache-dir={{ rclone_vfs_cache_dir_lookup }} \
{% endif %}
--config={{ rclone_config_path }} \
--read-only \
--exclude="**/.ignore" \
--use-server-modtime \
--no-checksum \
--s3-no-check-bucket \
--s3-disable-checksum \
--s3-no-head \
--checkers 16 \
--transfers 16 \
--dir-cache-time 9999h \
--fast-list \
--poll-interval 0 \
--rc \
--rc-addr 127.0.0.1:{{ rclone_remote_port }} \
--rc-no-auth \
{% if item.settings.vfs_cache.enabled %}
--vfs-cache-min-free-space={{ rclone_vfs_cache_min_free_space }} \
--vfs-cache-mode minimal \
--vfs-cache-max-size {{ item.settings.vfs_cache.size | default('300G') }} \
--vfs-cache-max-age {{ item.settings.vfs_cache.max_age | default('72h') }} \
--vfs-cache-poll-interval 10s \
--vfs-fast-fingerprint \
{% else %}
--vfs-cache-mode off \
{% endif %}
--buffer-size 16M \
--vfs-read-ahead 128M \
--vfs-read-chunk-size 64M \
--vfs-read-chunk-streams 16 \
--vfs-read-chunk-size-limit 2G \
-v \
"{{ rclone_remote_with_path }}" "/mnt/remote/{{ rclone_remote_name }}"
ExecStartPost=/bin/sh -c 'sleep 5 && for d in /mnt/remote/{{ rclone_remote_name }}/*/; do /usr/bin/rclone rc vfs/refresh dir="$$(basename "$$d")" recursive=false _async=true --rc-addr 127.0.0.1:{{ rclone_remote_port }}; done'
ExecStop=/bin/fusermount3 -uz "/mnt/remote/{{ rclone_remote_name }}"
Restart=on-failure
RestartSec=30
StartLimitInterval=120s
StartLimitBurst=2
[Install]
WantedBy=default.target
The ExecStartPost line is the parallel per-directory cache prime from above —
already in its systemd form ($$ instead of $). The Jinja2 variables
({{ rclone_remote_name }}, {{ rclone_remote_port }}, …) are filled in by
Saltbox when it renders the unit.
3. Point your remote at the template in /srv/git/saltbox/settings.yml:
rclone:
enabled: yes
remotes:
- remote: zendrive
settings:
mount: yes
template: /opt/mount-templates/custom/generic-scan.j2
union: yes
upload: no
upload_from: /mnt/local/Media
vfs_cache:
enabled: yes
max_age: 72h
size: 300G
version: latest
4. Apply and verify:
sb install mounts
systemctl status saltbox_managed_rclone_zendrive.service
# after ~30s, the parallel prime jobs should be running:
rclone rc job/list --rc-addr 127.0.0.1:5572
ZenLocalPoller port
Saltbox assigns each remote its RC port (first remote: 5572). Make sure
ZenLocalPoller's rclone.port matches the port in
the rendered unit's --rc-addr (see the
config reference).
s3cmd¶
# ~/.s3cfg
access_key = YOUR_ACCESS_KEY
secret_key = YOUR_SECRET_KEY
host_base = YOUR-ENDPOINT
host_bucket = YOUR-ENDPOINT
use_https = True
s3cmd ls
s3cmd get s3://my-bucket/file.txt ./
Cyberduck¶
- New Bookmark → choose Amazon S3.
- Set Server to your endpoint hostname (no
https://). - Enter your access key as the username and secret key as the password.
- Under More Options → Connect Mode, prefer path-style if offered.
boto3 (Python)¶
import boto3
s3 = boto3.client(
"s3",
endpoint_url="https://YOUR-ENDPOINT/",
aws_access_key_id="YOUR_ACCESS_KEY",
aws_secret_access_key="YOUR_SECRET_KEY",
region_name="us-east-1",
)
print([b["Name"] for b in s3.list_buckets()["Buckets"]])
Force path-style in SDKs
If an SDK defaults to virtual-host addressing, enable path-style (e.g. boto3's
Config(s3={"addressing_style": "path"})). See addressing.
Generate your config with an AI assistant¶
Don't want to hand-edit config files? Paste the prompt below into an AI assistant (Claude, Codex, ChatGPT, …), fill in the bracketed values, and it will produce the exact config for your tool.
I'm connecting to ZenDRIVE S3 Access — a read-only, S3-compatible endpoint for
streaming a media library. Generate a complete, ready-to-paste client config for me.
Reference these pages:
https://wiki.clearstreamer.com/connecting/clients/
https://wiki.clearstreamer.com/connecting/addressing/
My details:
- S3 endpoint URL: [from your welcome email, e.g. https://your-node.example/]
- Access key: [paste]
- Secret key: [paste — treat this like a password]
- Tool: [aws CLI / rclone / s3cmd / boto3 / Cyberduck / other]
- (rclone only) also give me an `rclone mount` command tuned for streaming? [yes/no]
Output the full config file/commands for my tool, then the first command to list my buckets.
Rules you must follow:
- The service is READ-ONLY: only list and download (GET). Never produce upload,
delete, sync-up, or bucket-creation commands.
- Use PATH-STYLE addressing (e.g. rclone `force_path_style = true`, boto3
`addressing_style: path`); virtual-host addressing fails here.
- For an rclone streaming mount, use S3-tuned settings with `minimal` cache mode:
`--read-only --vfs-cache-mode minimal --vfs-read-chunk-streams 16
--vfs-read-chunk-size 4M --buffer-size 32M --use-server-modtime
--dir-cache-time 1000h --poll-interval 0 --fast-list`. (`minimal` streams reads
without pulling whole files to disk, which keeps bandwidth controlled during library
scans; only use `--vfs-cache-mode full` with a sized `--vfs-cache-max-size` if the
user wants disk-buffered seeking and has bandwidth headroom.) Also expose
`--rc --rc-addr 127.0.0.1:5572 --rc-no-auth`
so ZenLocalPoller can refresh the cache on changes (the RC port must match the
poller's `rclone.port`).
Reference: https://wiki.clearstreamer.com/connecting/clients/#mount-for-streaming-rclone-vfs
- To make browsing fast after a reboot, after mounting warm the cache once with a light
NON-RECURSIVE refresh PER TOP-LEVEL DIRECTORY of the mount (they run in parallel; this
is the recommended default and stays easy on the backend):
`for d in /MOUNTPOINT/*/; do rclone rc vfs/refresh dir="$(basename "$d")"
recursive=false _async=true --rc-addr 127.0.0.1:5572; done`
(in a systemd ExecStartPost line, escape each `$` as `$$`). Only use `recursive=true`
if the user explicitly wants a full deep prime of the whole tree for a complete first
scan — it is heavy on a library this size. Add `--fast-list` to direct
`rclone copy`/`ls`/`size` commands too.
- If a region is required and I don't have one, use `us-east-1` as a placeholder.
- Never hardcode my secret key into a shared or committed file, and never log it.
- Remind me the endpoint only works from the single whitelist IP I set in the Client Area.
- Don't invent credentials — leave placeholders for anything I didn't provide.
Does my favorite client work?¶
If it speaks the S3 API and lets you set a custom endpoint and keys, it should work. If you hit trouble, see client gotchas or open a support ticket.