This guide demonstrates common TigerFS workflows using real-world scenarios.
- TigerFS installed (
go install github.com/timescale/tigerfs/cmd/tigerfs@latest) - PostgreSQL database with data
- Linux: FUSE support (usually pre-installed)
- macOS: No additional dependencies (uses native NFS backend)
Scenario: You want to try TigerFS without setting up a local database.
The Docker demo runs both TigerFS and PostgreSQL in containers with sample data (1,000 users, 200 products, 8,000 orders).
# From the TigerFS repository root
cd scripts/demo
# Start the demo (builds containers, starts PostgreSQL, mounts TigerFS)
./demo.sh start --docker
# Enter the container (starts in /mnt/db)
./demo.sh shell
# Explore!
ls
cat users/1.json
cat products/1.json
ls orders/.first/10/$ ls
categories/ orders/ products/ users/
$ cat users/1.json
{"id":1,"name":"Alice Smith","first_name":"Alice","last_name":"Smith",...}
The demo.sh script handles everything: building containers, waiting for PostgreSQL to be ready, and mounting TigerFS automatically. When you run ./demo.sh shell, you're placed directly in /mnt/db where the database is mounted.
./demo.sh status # Check if running
./demo.sh restart # Stop and start again
./demo.sh stop # Unmount and stop containersScenario: You're on macOS and want TigerFS to run natively (not in Docker) for better performance and integration.
The macOS demo runs TigerFS natively using the NFS backend, with only PostgreSQL in Docker.
- macOS (Apple Silicon or Intel)
- Docker Desktop
- Go 1.21+ (for building TigerFS)
# From the TigerFS repository root
cd scripts/demo
# Start the demo (starts PostgreSQL, builds TigerFS, mounts at /tmp/tigerfs-demo)
./demo.sh start --mac
# Explore directly from your Mac terminal
ls /tmp/tigerfs-demo
cat /tmp/tigerfs-demo/users/1.json
cat /tmp/tigerfs-demo/products/1.json$ ls /tmp/tigerfs-demo
categories/ orders/ products/ users/
$ cat /tmp/tigerfs-demo/users/1.json
{"id":1,"name":"Alice Smith","first_name":"Alice","last_name":"Smith",...}
Unlike the Docker demo, TigerFS runs as a native macOS process. This means:
- The mount appears at
/tmp/tigerfs-demoon your Mac (not inside a container) - You can use any Mac tools directly (Finder, VS Code, your favorite editor)
./demo.sh stopScenario: You have PostgreSQL running directly on your machine (not in Docker) and want to explore it.
# Create a mount point
mkdir -p /mnt/db
# Mount with a connection string
tigerfs mount postgres://user:password@localhost:5432/mydb /mnt/db &
# Or mount using environment variables
export PGHOST=localhost
export PGPORT=5432
export PGUSER=user
export PGPASSWORD=password
export PGDATABASE=mydb
tigerfs mount /mnt/db &
# Verify the mount
ls /mnt/dbyour_table1/
your_table2/
...
TigerFS connects to the PostgreSQL database and presents each table in the default schema (typically public) as a directory. The mount runs in the background (&) so you can continue using the terminal.
The mount point /mnt/db now acts as a window into your database - each subdirectory is a table, and you can navigate into them to access rows and columns.
# When finished
tigerfs unmount /mnt/dbScenario: You've connected to an unfamiliar database and want to understand its structure without writing SQL.
# List all tables
ls /mnt/db
# See hidden metadata files
ls -la /mnt/db/users
# View table schema (CREATE TABLE statement)
cat /mnt/db/users/.info/schema
# List columns
cat /mnt/db/users/.info/columns
# Check row count
cat /mnt/db/users/.info/count
# View indexes
cat /mnt/db/users/.info/indexes$ ls /mnt/db
orders/ products/ users/
$ ls -la /mnt/db/users
total 0
drwxr-xr-x 1 root root 0 Jan 26 12:00 .
drwxr-xr-x 1 root root 0 Jan 26 12:00 ..
drwxr-xr-x 1 root root 0 Jan 26 12:00 .by
drwxr-xr-x 1 root root 0 Jan 26 12:00 .first
drwxr-xr-x 1 root root 0 Jan 26 12:00 .info
drwxr-xr-x 1 root root 0 Jan 26 12:00 .sample
-rw-r--r-- 1 root root 64 Jan 26 12:00 1
-rw-r--r-- 1 root root 64 Jan 26 12:00 2
...
$ cat /mnt/db/users/.info/schema
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
age INTEGER,
active BOOLEAN DEFAULT true,
bio TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
$ cat /mnt/db/users/.info/columns
id
name
email
age
active
bio
created_at
$ cat /mnt/db/users/.info/count
1000
$ cat /mnt/db/users/.info/indexes
PRIMARY KEY: id
UNIQUE: emailTigerFS exposes database metadata under the .info/ directory:
.info/schema- The complete CREATE TABLE statement.info/columns- Simple list of column names in schema order.info/count- Total number of rows (runsSELECT COUNT(*)).info/indexes- Available indexes for efficient lookups
Dot-directories are hidden by default with ls but visible with ls -a or ls -la. This keeps the view clean while making metadata accessible.
Scenario: You need to read row data and want to choose the most convenient format for your use case.
# Read a row as TSV (default, no extension)
cat /mnt/db/users/1
# Read a row as TSV (explicit extension)
cat /mnt/db/users/1.tsv
# Read a row as JSON
cat /mnt/db/users/1.json
# Read a row as CSV
cat /mnt/db/users/1.csv
# Read individual columns (row-as-directory)
cat /mnt/db/users/1/email
cat /mnt/db/users/1/name
cat /mnt/db/users/1/age$ cat /mnt/db/users/1
1 Alice Johnson alice@example.com 28 true Software engineer who loves hiking. 2024-01-15T10:30:00Z
$ cat /mnt/db/users/1.json
{"id":1,"name":"Alice Johnson","email":"alice@example.com","age":28,"active":true,"bio":"Software engineer who loves hiking.","created_at":"2024-01-15T10:30:00Z"}
$ cat /mnt/db/users/1.csv
1,Alice Johnson,alice@example.com,28,true,Software engineer who loves hiking.,2024-01-15T10:30:00Z
$ cat /mnt/db/users/1/email
alice@example.com
$ cat /mnt/db/users/1/name
Alice Johnson
$ cat /mnt/db/users/1/age
28TigerFS supports three data formats, selected by file extension:
| Extension | Format | Use Case |
|---|---|---|
(none) or .tsv |
Tab-separated values | Shell pipelines, cut, awk |
.json |
JSON object | APIs, jq, programming languages |
.csv |
Comma-separated values | Spreadsheets, data tools |
Row-as-file (/users/1.json) returns the entire row in one read operation - efficient for complete row access.
Row-as-directory (/users/1/email) treats the row as a directory where each column is a separate file - useful for reading or updating individual fields.
Both representations exist simultaneously for every row.
Scenario: You need to update a user's email address and insert a new product.
# Update a single column
echo "newemail@example.com" > /mnt/db/users/1/email
# Verify the update
cat /mnt/db/users/1/email
# Update specific columns via JSON (PATCH semantics - only specified keys updated)
echo '{"name":"Alice Smith","email":"alice.smith@example.com"}' > /mnt/db/users/1.json
# Update specific columns via TSV (PATCH semantics - header specifies columns)
echo -e 'name\temail\nAlice Smith\talice.smith@example.com' > /mnt/db/users/1.tsv
# Insert a new row (use next available ID or specify one)
echo '{"name":"New Product","price":29.99,"in_stock":true,"category":"gadgets"}' > /mnt/db/products/new.json
# Delete a row
rm /mnt/db/users/999$ echo "newemail@example.com" > /mnt/db/users/1/email
$ cat /mnt/db/users/1/email
newemail@example.com
$ echo '{"name":"Alice Smith","email":"alice.smith@example.com"}' > /mnt/db/users/1.json
$ cat /mnt/db/users/1.json
{"id":1,"name":"Alice Smith","email":"alice.smith@example.com","age":28,"active":true,"bio":"Software engineer who loves hiking.","created_at":"2024-01-15T10:30:00Z"}TigerFS translates filesystem operations to SQL:
| Operation | SQL Generated |
|---|---|
echo "x" > /users/1/email |
UPDATE users SET email = 'x' WHERE id = 1 |
echo '{...}' > /users/1.json |
UPDATE users SET name=..., email=... WHERE id = 1 |
echo -e 'col\nval' > /users/1.tsv |
UPDATE users SET col = 'val' WHERE id = 1 |
echo '{...}' > /products/new.json |
INSERT INTO products (...) VALUES (...) |
rm /users/999 |
DELETE FROM users WHERE id = 999 |
PATCH Semantics: All format extensions (.json, .yaml, .csv, .tsv) use PATCH semantics - only the columns you specify are updated. Columns not included retain their existing values.
Important notes:
- Updates respect database constraints (NOT NULL, UNIQUE, foreign keys)
- Writes fail with appropriate errors if constraints are violated
- TSV/CSV writes require a header row specifying column names
- Delete operations are permanent (no trash/undo)
Scenario: You want to find specific records and analyze data using familiar Unix tools.
# Find users with "alice" in their email
grep -r "alice" /mnt/db/users/*/email
# List all active users (TSV format, 5th column is 'active')
for id in $(ls /mnt/db/users | head -100); do
row=$(cat /mnt/db/users/$id)
active=$(echo "$row" | cut -f5)
if [ "$active" = "true" ]; then
echo "$row"
fi
done
# Calculate average age using awk
for id in $(ls /mnt/db/users | head -100); do
cat /mnt/db/users/$id/age
done | awk '{sum+=$1; count++} END {print "Average age:", sum/count}'
# Find products over $50 using jq
for id in $(ls /mnt/db/products); do
cat /mnt/db/products/$id.json
done | jq -s '[.[] | select(.price > 50)] | length'
# Sort users by age using jq
for i in $(ls /mnt/db/users | head -20); do
cat /mnt/db/users/$i.json
done | jq -s 'sort_by(.age)'
# Find orders with specific status
for id in $(ls /mnt/db/orders | head -1000); do
status=$(cat /mnt/db/orders/$id/status)
if [ "$status" = "pending" ]; then
echo "Order $id is pending"
fi
done$ grep -r "alice" /mnt/db/users/*/email
/mnt/db/users/1/email:alice@example.com
/mnt/db/users/47/email:alice.jones@company.com
$ for id in $(ls /mnt/db/users | head -100); do cat /mnt/db/users/$id/age; done | awk '{sum+=$1; count++} END {print "Average age:", sum/count}'
Average age: 34.7
$ for id in $(ls /mnt/db/products); do cat /mnt/db/products/$id.json; done | jq -s '[.[] | select(.price > 50)] | length'
47TigerFS enables database queries using standard Unix tools:
- grep - Search for patterns across rows
- awk - Process columnar data, calculate aggregates
- cut - Extract specific columns from TSV/CSV
- jq - Filter, transform, and analyze JSON data
- sort - Order results
- uniq - Find distinct values
- wc - Count matching rows
Performance tips:
- Use
.first/N/or.sample/N/paths to limit rows for exploration - Use index paths (
.email/value) for indexed lookups instead of scanning - Combine with
headto limit iterations
Scenario: You have a database on Tiger Cloud or Ghost and want to mount, create, or fork it using TigerFS.
- Tiger CLI installed:
curl -fsSL https://cli.tigerdata.com | sh(or Ghost CLI for Ghost) - Authenticate:
tiger auth login(orghost loginfor Ghost)
# Mount Tiger Cloud service by ID
tigerfs mount tiger:abcde12345 /mnt/cloud
# Mount Ghost database by ID
tigerfs mount ghost:fghij67890 /mnt/cloud
# Explore your database
ls /mnt/cloud# Create a Tiger Cloud database and auto-mount it
tigerfs create tiger:my-new-db
# Create with auto-generated name
tigerfs create tiger:
# Create without mounting
tigerfs create tiger:my-db --no-mount# Fork a mounted database
tigerfs fork /mnt/cloud my-experiment
# Fork by service ID
tigerfs fork tiger:abcde12345 my-experiment
# Fork without mounting
tigerfs fork /mnt/cloud --no-mount# Show info about a mounted filesystem and its backing service
tigerfs info /mnt/cloud
# JSON output for scripting
tigerfs info --json /mnt/cloud# Set credentials (from Tiger Cloud Console)
export TIGER_PUBLIC_KEY=<your-public-key>
export TIGER_SECRET_KEY=<your-secret-key>
export TIGER_PROJECT_ID=<your-project-id>
# Authenticate using environment variables
tiger auth login
# Mount using tiger: prefix
tigerfs mount tiger:<your-service-id> /mnt/cloudTigerFS integrates with Tiger Cloud and Ghost through their CLIs using a prefix scheme:
- Prefix —
tiger:IDorghost:IDtells TigerFS which backend to use - Authentication — handled by the backend CLI (
tiger auth loginorghost login) - Connection — TigerFS calls the backend CLI to retrieve the connection string
- Management —
create,fork, andinfocommands work through the same prefix scheme
Configuration: Set default_backend: tiger (or ghost) in ~/.config/tigerfs/config.yaml to omit the prefix. See ADR-013 for the full prefix scheme.
# Basic mount
tigerfs mount postgres://user:pass@host:5432/db /mnt/db &
# Using environment variables
tigerfs mount /mnt/db &
# Tiger Cloud / Ghost
tigerfs mount tiger:<service-id> /mnt/db &
tigerfs mount ghost:<service-id> /mnt/db &
# With debug logging
tigerfs mount --debug postgres://... /mnt/db &
# Unmount
tigerfs unmount /mnt/db| Path | Description |
|---|---|
/mnt/db/ |
List all tables |
/mnt/db/users/ |
List rows in table |
/mnt/db/users/123 |
Row as TSV |
/mnt/db/users/123.json |
Row as JSON |
/mnt/db/users/123/email |
Single column |
/mnt/db/users/.info/schema |
Table DDL |
/mnt/db/users/.info/count |
Row count |
/mnt/db/users/.first/100/ |
First 100 rows |
/mnt/db/users/.sample/50/ |
Random 50 rows |
/mnt/db/users/.by/email/foo@x.com/ |
Index lookup |
| Extension | Format | Best For |
|---|---|---|
| (none) | TSV | Shell scripts, awk, cut |
.tsv |
TSV | Explicit TSV |
.csv |
CSV | Spreadsheets, pandas |
.json |
JSON | APIs, jq, web apps |