Skip to content

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

  1. New Bookmark → choose Amazon S3.
  2. Set Server to your endpoint hostname (no https://).
  3. Enter your access key as the username and secret key as the password.
  4. 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.