TiDB Cloud Filesystem CLI Command Reference
Use ti fs to provision TiDB Cloud Filesystem resources and access their data from commands or local mounts.
Command tree
ti fs
├── create-file-system
├── import-file-system-token
├── generate-file-system-token
├── generate-file-system-scoped-token
├── list-file-system-tokens
├── enable-file-system-token
├── disable-file-system-token
├── delete-file-system-token
├── refresh-file-system-token
├── list-file-systems
├── describe-file-system
├── check-file-system
├── delete-file-system
├── copy-file
├── read-file
├── list-files
├── describe-file
├── move-file
├── delete-file
├── create-directory
├── chmod-file
├── create-symlink
├── create-hardlink
├── search-file-content
├── find-files
├── create-layer
├── list-layers
├── fork-layer
├── list-layer-chain
├── describe-layer
├── diff-layer
├── create-layer-checkpoint
├── delete-layer
├── rollback-layer
├── commit-layer
├── pack-file-system
├── unpack-file-system
├── mount-file-system
├── drain-file-system
└── unmount-file-system
Command details
Resource commands
Data and namespace commands
Layer and portability commands
Mount commands
Prerequisites
- Run
ti configurebefore provisioning, listing, describing, or deleting Filesystems. - Install
tiwith the release installer so theti-drive9companion is next to thetibinary. - Install
jqto run the JSON extraction examples as written, or use an equivalent JSON processor. - Treat the returned FS owner token as a secret.
Data-plane commands can instead use an existing Filesystem with only TI_FS_TOKEN and TI_REGION_CODE, without TiDB Cloud API keys. TI_FS_FILE_SYSTEM_ID is an optional assertion.
Manage Filesystem resources
Create a resource, wait until data-plane access is ready, and save the server-assigned ID and one-time owner token without making the file world-readable:
umask 077
ti fs create-file-system \
--display-name agent-workspace \
--label environment=development \
--wait > ./filesystem.json
export TI_FS_FILE_SYSTEM_ID="$(jq -r '.file_system_id' ./filesystem.json)"
export TI_FS_TOKEN="$(jq -r '.fs_token' ./filesystem.json)"
Without --wait, ti returns after Drive9 accepts provisioning. With the flag, ti waits up to 10 minutes until the root is readable through the public Drive9 data-plane CLI. A failed wait leaves the resource and locally stored credential intact.
The JSON response includes fs_token exactly once. Store it in a secret manager, then delete filesystem.json. A configured machine can use the locally stored credential by ID without exporting the token. Display names and labels are organization-visible inventory metadata, not resource selectors. Do not store credentials, connection strings, private paths, personal data, or other secrets in labels.
List remote resources in the effective region and describe one by ID:
ti fs list-file-systems
ti fs describe-file-system --file-system-id <file-system-id>
Filter the inventory by a display-name substring and one exact label:
ti fs list-file-systems \
--display-name workspace \
--label environment=development
List and describe results include authoritative display metadata, status, region, quota and usage. has_local_token reports only whether the selected local profile has a matching token; token values are never included.
Select a resource for subsequent commands in the current shell:
export TI_FS_FILE_SYSTEM_ID="<file-system-id>"
Check the selected resource and companion:
ti fs check-file-system --file-system-id <file-system-id>
Delete a resource only after removing data you need:
ti fs delete-file-system \
--file-system-id <file-system-id>
Create and delete support --dry-run. Deletion requires TiDB Cloud API keys and an ID, but not a local FS token. Drive9 deletion is asynchronous, so a successfully accepted request reports status: "deleting" while ti removes only a matching ID-keyed local credential.
Manage Filesystem tokens
One Filesystem can have multiple owner or scoped tokens. The remote service is authoritative for token inventory and lifecycle state. Each local profile stores only one selected operational token per Filesystem; it does not mirror every remote token.
An owner FS token grants Filesystem use and token-management capabilities, but it does not grant TiDB Cloud resource administration. Generating another owner token requires TiDB Cloud API credentials and an explicit Filesystem ID. Listing, enabling, disabling, and deleting tokens can instead use an owner token; in that mode, ti derives the Filesystem ID from the token and --file-system-id is optional. Creating, listing, describing, and deleting Filesystem resources always require TiDB Cloud API credentials, and Filesystem deletion always requires an explicit --file-system-id.
Generate an additional owner token for CI and save its one-time plaintext response securely:
umask 077
ti fs generate-file-system-token \
--file-system-id "<file-system-id>" \
--token-name ci-deploy \
--ttl 24h > ./ci-token.json
Generation does not change local selection by default. Add --store-locally to select the generated token. If another local token exists, --replace is also required. Replacing local selection does not disable or revoke the old remote token.
List token metadata and use the immutable token ID for state changes:
ti fs list-file-system-tokens --file-system-id "<file-system-id>" --output text
ti fs disable-file-system-token --file-system-id "<file-system-id>" --token-id "<token-id>"
ti fs enable-file-system-token --file-system-id "<file-system-id>" --token-id "<token-id>"
ti fs delete-file-system-token --file-system-id "<file-system-id>" --token-id "<token-id>"
Token names are not unique. List output never contains token plaintext, and revoked tokens do not appear. Authentication changes can take approximately 10 seconds to converge.
Refresh a selected local token atomically:
ti fs refresh-file-system-token --file-system-id "<file-system-id>"
To refresh a token held by an external secret manager, supply TI_FS_TOKEN and TI_REGION_CODE. The command returns the replacement plaintext but cannot update the external store. Refresh is non-idempotent: when the request might have committed but the response was lost, do not retry with the old token. Generate another owner token with TiDB Cloud credentials instead.
Before refreshing, disabling, or deleting a token used by a known local mount, drain and unmount it:
ti fs drain-file-system --mount-path /path/to/workspace
ti fs unmount-file-system --mount-path /path/to/workspace
Older credentials created or imported before token lifecycle metadata was available can continue to access data, but ti cannot correlate them with a remote token row. It never guesses a token ID from name, timestamp, or list order.
Select one of multiple Filesystems
One profile can own multiple resources. Selection precedence is:
--file-system-id;TI_FS_FILE_SYSTEM_ID;- the ID embedded in an explicitly supplied FS token;
- otherwise fail with
fs.missing_file_system_id.
ti does not infer a resource from local credential count, even when only one credential exists. This makes scripts deterministic when resources are added or removed.
Copy and read data
Upload, download, and copy remotely:
ti fs copy-file --from-local ./README.md --to-remote /workspace/README.md
ti fs copy-file --from-remote /workspace/README.md --to-local ./README.copy.md --create-parents
ti fs copy-file --from-remote /workspace/README.md --to-remote /archive/README.md
Use --overwrite to replace an existing target, --resume for a supported interrupted upload or download, and --recursive for directories:
ti fs copy-file --from-local ./src --to-remote /workspace/src --recursive
ti fs copy-file --from-local ./large.bin --to-remote /workspace/large.bin --resume
Append and stream:
ti fs copy-file --from-local ./tail.log --to-remote /logs/app.log --append
printf 'hello\n' | ti fs copy-file --from-stdin --to-remote /workspace/stdin.txt
ti fs copy-file --from-remote /workspace/stdin.txt --to-stdout
Add metadata during upload:
ti fs copy-file \
--from-local ./report.md \
--to-remote /workspace/report.md \
--tag owner=agent \
--tag stage=review \
--description "agent review report"
Read a complete file or a byte range:
ti fs read-file --path /workspace/report.md
ti fs read-file --path /workspace/large.bin --offset 1024 --length 4096
Inspect and modify the namespace
ti fs list-files --path /workspace
ti fs describe-file --path /workspace/report.md
ti fs create-directory --path /workspace/archive --mode 0755
ti fs move-file --from-remote /workspace/report.md --to-remote /workspace/archive/report.md
ti fs chmod-file --path /workspace/archive/report.md --mode 0600
ti fs create-symlink --target archive/report.md --link-path /workspace/report.link
ti fs create-hardlink --source-path /workspace/archive/report.md --link-path /workspace/report.hard
ti fs delete-file --path /workspace/report.link
ti fs delete-file --path /workspace/archive --recursive
Mutating namespace commands support --dry-run.
Search content and metadata:
ti fs search-file-content --path /workspace --pattern "TODO" --limit 50
ti fs find-files --path /workspace --file-name-pattern "*.md" --tag stage=review
find-files also supports resource type, time, size, and result-limit filters. Both search commands accept --layer-id.
Use layers and checkpoints
A layer records changes over a base root before you commit or discard them. Use copy-file --layer-id for individual files. Recursive copy and --layer-id are mutually exclusive; seed a directory tree through a writable FUSE layer mount instead.
ti fs create-layer \
--base-root-path /workspace \
--layer-name agent-task \
--durability-mode restore-safe \
--tag task=review
Use the returned layer ID for individual file writes and inspection:
ti fs copy-file \
--from-local ./proposal.md \
--to-remote /workspace/proposal.md \
--layer-id "<layer-id>"
ti fs list-layers
ti fs describe-layer --layer-id "<layer-id>"
ti fs diff-layer --layer-id "<layer-id>"
ti fs create-layer-checkpoint \
--layer-id "<layer-id>" \
--checkpoint-id seed \
--label "before review"
Fork independent copy-on-write timelines from the checkpoint, inspect their ancestry, and mount a child through FUSE:
ti fs fork-layer \
--parent-layer-ref "<layer-id>" \
--layer-name experiment \
--checkpoint-id seed
ti fs list-layer-chain --layer-ref experiment
ti fs mount-file-system \
--file-system-id <file-system-id> \
--mount-path /path/to/experiment \
--remote-path /workspace \
--driver fuse \
--layer-ref experiment
Drain and unmount a writable layer before creating a checkpoint, rolling it back, or committing it. Finish the layer by rolling it back or committing it:
ti fs rollback-layer --layer-id "<layer-id>"
ti fs commit-layer --layer-id "<layer-id>"
These two commands represent alternative outcomes for the same work; do not run both in sequence in a real workflow.
Pack local overlay state
FUSE mount profiles can route selected paths to local overlay storage. Pack those paths to a remote archive before moving to another machine:
ti fs pack-file-system --mount-path /path/to/workspace
ti fs unpack-file-system --mount-path /path/to/workspace
Without an active mount, provide --local-root, --remote-root, and --mount-profile. --archive-path selects the remote archive, repeatable --path limits pack contents, and --no-replace makes unpack merge rather than replace manifest paths.
Mount a Filesystem
Create the local mount path and mount in the background:
mkdir -p /path/to/workspace
ti fs mount-file-system \
--file-system-id <file-system-id> \
--mount-path /path/to/workspace
The default --driver auto is platform-specific. --remote-path exposes a subtree, and --read-only prevents writes. The command starts the mount runtime in the background, waits until it is ready, and then returns. Use ti fs unmount-file-system to end the mount.
Platform behavior
Mount in Docker and Docker Compose
Installing FUSE3 inside an image does not enable mounts by itself. The Docker host must provide /dev/fuse, and the container must receive permission to perform the mount. The following Dockerfile installs the required Ubuntu package and ti without storing any cloud or Filesystem credentials in the image:
FROM ubuntu:24.04
ARG TI_VERSION=latest
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates curl fuse3 \
&& rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://github.com/tidbcloud/ti-cli/releases/latest/download/install.sh \
| sh -s -- --yes --version "${TI_VERSION}"
ENV PATH="/root/.ti/bin:${PATH}"
RUN mkdir -p /workspace
CMD ["bash"]
Build the image, then pass the Filesystem owner token and canonical region code at runtime:
docker build -t ti-fuse .
docker run --rm -it \
--device /dev/fuse \
--cap-add SYS_ADMIN \
--security-opt apparmor=unconfined \
--env TI_FS_TOKEN \
--env TI_REGION_CODE \
ti-fuse
The two environment variables must already exist in the host shell. Inside the container, mount and use the Filesystem normally:
ti fs mount --mount-path /workspace
printf 'hello from Docker\n' > /workspace/hello.txt
ti fs umount --mount-path /workspace
Use the equivalent runtime settings in compose.yaml:
services:
agent:
build:
context: .
args:
TI_VERSION: latest
devices:
- /dev/fuse:/dev/fuse
cap_add:
- SYS_ADMIN
security_opt:
- apparmor=unconfined
environment:
TI_FS_TOKEN: ${TI_FS_TOKEN}
TI_REGION_CODE: ${TI_REGION_CODE}
TI_FS_FILE_SYSTEM_ID: ${TI_FS_FILE_SYSTEM_ID}
stdin_open: true
tty: true
Start an interactive container with:
docker compose run --rm agent
fuse3 provides /usr/bin/fusermount3. If mounting reports fusermount3: mount failed: Permission denied, confirm that the host has /dev/fuse and that all required devices, cap_add, and AppArmor settings reached the container. apparmor=unconfined applies to AppArmor-enabled hosts such as Ubuntu and can be omitted where AppArmor is not active.
macOS intentionally keeps WebDAV as the automatic choice even when macFUSE is installed. To use FUSE, install a supported release from the official macFUSE site, complete any approval or restart requested by its installer, and run:
ti fs mount-file-system \
--file-system-id <file-system-id> \
--mount-path /path/to/workspace \
--driver fuse
Explicit FUSE supports cache controls:
ti fs mount-file-system \
--file-system-id <file-system-id> \
--mount-path /path/to/workspace \
--driver fuse \
--cache-dir "$HOME/.ti/cache/workspace" \
--read-cache-size-mb 256 \
--read-cache-max-file-mb 16 \
--read-cache-ttl 30s
Ubuntu 26.04 mount paths
Ubuntu 26.04 enforces an AppArmor profile for /usr/bin/fusermount3. The default profile allows FUSE mounts under the current user's home directory, /mnt, /media, /tmp, and /run/user/<uid>, but not directly under /workspace. This restriction applies to root as well as non-root users and produces an error similar to /usr/bin/fusermount3: mount failed: Permission denied.
Prefer an allowed mount path:
mkdir -p "$HOME/workspace"
ti fs mount-file-system \
--file-system-id <file-system-id> \
--mount-path "$HOME/workspace"
For a system-level path, /mnt/workspace is allowed by the default profile:
sudo mkdir -p /mnt/workspace
sudo chown "$(id -u):$(id -g)" /mnt/workspace
ti fs mount-file-system \
--file-system-id <file-system-id> \
--mount-path /mnt/workspace
If an application requires /workspace, add the following rules to /etc/apparmor.d/local/fusermount3, and then reload the profile with sudo apparmor_parser -r /etc/apparmor.d/fusermount3:
mount fstype=@{fuse_types} options=(nosuid,nodev) options in (ro,rw,noatime,dirsync,nodiratime,noexec,sync) -> /workspace/{,**/},
umount /workspace/{,**/},
The default mount profile is coding-agent, which keeps common development state such as dependencies, caches, generated output, and Git internals in a local overlay. Those local-only files do not survive machine deletion unless you pack or preserve the local volume. Use --mount-profile portable when you want automatic portable pack behavior, or none when you do not want the coding-agent overlay policy.
Drain and unmount
Stop writers and close open files before cleanup. A normal unmount performs a graceful shutdown: the companion flushes open handles and pending FUSE write-back work, waits for its upload queues, and then exits. You do not need to run drain first:
ti fs unmount-file-system \
--mount-path /path/to/workspace
Use drain when you need an explicit durability barrier while keeping a FUSE mount online, for example before handing the mount to another process or checking remote visibility:
ti fs drain-file-system \
--mount-path /path/to/workspace \
--timeout 30s
Drain flushes dirty handles and waits for pending writes, but the mount remains available and can accept new writes afterward. It is not supported for WebDAV. unmount-file-system also supports --timeout, --force, --ignore-absent, --pack-archive-path, and --no-auto-pack.
A successful background mount writes a non-secret locator under ~/.ti/mounts/. Drain and unmount can use that locator from the same HOME without TI_FS_TOKEN or TI_REGION_CODE.
Unix-style aliases
Aliases change only the command name. All flags remain long and identical to the canonical command.