CtrlK
BlogDocsLog inGet started
Tessl Logo

netapp-volumes

Work with Domino Volumes for NetApp ONTAP - enterprise-grade, multi-terabyte storage with near-instant snapshots. Covers volume creation, snapshot versioning with commit messages, cross-project sharing, mount paths (/mnt/netapp-volumes/ or /domino/netapp-volumes/), and the NetApp Volumes REST API. Use when managing large-scale data storage, needing fast no-copy snapshots, or integrating existing NetApp ONTAP infrastructure with Domino.

65

Quality

78%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Fix and improve this skill with Tessl

tessl review fix ./skills/netapp-volumes/SKILL.md
SKILL.md
Quality
Evals
Security

Domino NetApp Volumes Skill

Description

This skill helps users work with Domino Volumes for NetApp ONTAP — enterprise-grade storage that mounts NetApp ONTAP filesystems directly into Domino workloads, enabling multi-terabyte scale with near-instant snapshotting.

Activation

Activate this skill when users want to:

  • Create or manage NetApp Volumes in Domino
  • Work with NetApp volume snapshots and versioning
  • Share large-scale data across projects or teams
  • Access existing NetApp ONTAP storage from Domino workspaces, jobs, or apps
  • Use the NetApp Volumes REST API programmatically
  • Understand mount paths for NetApp volumes
  • Choose between NetApp Volumes and Domino Datasets

What is a Domino NetApp Volume?

A Domino NetApp Volume is:

  • Enterprise-grade storage: Backed by NetApp ONTAP via Kubernetes PVCs with the netapp-storage storage class
  • Multi-terabyte scale: No practical upper limit — suitable for very large datasets
  • Near-instant snapshots: ~3 seconds even for 100+ GB volumes using redirect-on-write (no extra storage consumed at snapshot time)
  • Versioned with commit messages: Snapshots support human-readable tags and commit messages, unlike Datasets
  • Shareable: Attach a single volume to multiple projects and teams
  • Persistent: Data persists across executions

NetApp Volumes vs. Domino Datasets

FeatureNetApp VolumesDomino Datasets
Storage backendExternal NetApp ONTAPDomino-managed NFS/EFS
Data scaleMulti-terabyte and beyondUp to ~1 TB
Snapshot speed~3 seconds (any size)Scales with data size
Snapshot storage costNo extra space (redirect-on-write)Duplicates physical data
Commit messages on snapshotsSupportedNot supported
Create new volume from snapshotRead-only clonesCan create editable dataset
Cross-project sharingYesYes
Admin prerequisiteAdmin must register filesystemsNone
REST APIFull dedicated APIVia Domino Python SDK

Use NetApp Volumes when:

  • Data exceeds ~1 TB
  • Near-instant snapshotting is required
  • Your organization already has NetApp ONTAP infrastructure
  • You need snapshot commit messages for audit trails
  • High-performance shared storage across many teams

Use Domino Datasets when:

  • Data is Domino-managed with no external infrastructure dependency
  • You need to create editable new versions from snapshots
  • Data is under ~1 TB

Prerequisites

An admin must register at least one NetApp filesystem (Kubernetes PVC with the netapp-storage label) before users can create volumes. Contact your Domino administrator if no filesystems are available.


Creating a NetApp Volume

From the Domino Home Page

  1. Navigate to Data > NetApp Volumes in the toolbar
  2. Click Add NetApp Volume > Create Volume
  3. Fill in the form:
    • Name: Letters, numbers, underscores, hyphens only
    • Description: Brief overview of the data
    • Data Plane: Select from available options
    • NetApp Filesystem: Choose a registered filesystem
    • Capacity: Set maximum storage allocation
  4. Click Next to configure permissions
  5. Assign users with Reader / Editor / Owner roles
  6. Click Finish
  7. Manually add the volume to projects afterward

From within a Project (recommended — auto-associates)

  1. Open a project → Data > NetApp Volumes (left panel)
  2. Click Add NetApp Volume > Create Volume
  3. Fill in the same form fields as above
  4. The volume is automatically associated with the current project on creation

Via REST API

import os
import requests

# Auth token from in-cluster token service
token = requests.get("http://localhost:8899/access-token").text.strip()
headers = {
    "Authorization": f"Bearer {token}",
    "Content-Type": "application/json"
}

api_url = os.environ["DOMINO_API_HOST"]
remotefs_url = os.environ["DOMINO_REMOTE_FILE_SYSTEM_HOSTPORT"]

# Look up your user ID (username is available in the DOMINO_USER_NAME env var)
user_resp = requests.get(
    f"{api_url}/v4/users?userName={os.environ['DOMINO_USER_NAME']}",
    headers=headers
).json()
user_id = user_resp[0]["id"]

# Create a volume (capacity is in bytes; grants is required)
response = requests.post(
    f"{remotefs_url}/remotefs/v1/volumes",
    headers=headers,
    json={
        "name": "large-training-data",
        "description": "Multi-TB training dataset for vision models",
        "filesystemId": "<filesystem-id>",
        "capacity": 5_000_000_000_000,  # 5 TB in bytes
        "grants": [
            {"targetId": user_id, "targetRole": "VolumeOwner"}
        ]
    }
)
volume = response.json()
print(f"Created volume ID: {volume['id']}")

Adding a Volume to a Project

Via Domino UI

  1. Go to project → Data > NetApp Volumes
  2. Click Add NetApp Volume > Add Existing Volume
  3. Select the volume from the list
  4. Configure access level for the project

Via REST API

# Attach a volume to a project
requests.post(
    f"{remotefs_url}/remotefs/v1/rpc/attach-volume-to-project",
    headers=headers,
    json={
        "volumeId": "<volume-id>",
        "projectId": "<project-id>"
    }
)

Mount Paths

Mount paths depend on your project type. Check which exists in your execution to determine your project type.

Git-Based Projects

Volume TypeMount Path
Live volume/mnt/netapp-volumes/<volume-name>/
Snapshot by number/mnt/netapp-volumes/snapshots/<volume-name>/<snapshot-number>/
Snapshot by tag/mnt/netapp-volumes/snapshot-tags/<volume-name>/<tag-name>/

DFS (Domino File System) Projects

Volume TypeMount Path
Live volume/domino/netapp-volumes/<volume-name>/
Snapshot by number/domino/netapp-volumes/snapshots/<volume-name>/<snapshot-number>/
Snapshot by tag/domino/netapp-volumes/snapshot-tags/<volume-name>/<tag-name>/

Snapshot Access Behavior

There are two ways to access snapshots, with an important behavioral difference:

  • /snapshots/<volume-name>/<number>/ — Accessed by snapshot number. When a new snapshot is taken while a workspace is running, the new numbered directory appears immediately in that workspace without a restart.
  • /snapshot-tags/<volume-name>/<tag-name>/ — Accessed by tag name. Each snapshot has at most one active tag path — a symlink to its numbered snapshot directory. If you apply multiple tags to the same snapshot, only the most recently applied tag creates a path; earlier tags for that snapshot are not accessible by path. Tag paths for new snapshots are also not visible in a running workspace — you must restart the workspace for them to appear.

Use the numbered path when you need to access a fresh snapshot from within a live workspace. Use the tagged path for stable, named references in reproducible runs.

Important: Each snapshot exposes only one tag path at a time — the most recently applied tag. Older tags on the same snapshot do not have accessible paths.

Important: Renaming a volume changes its mount path. Update any hardcoded paths in your code after renaming.

Identify Your Project Type

import os

if os.path.exists("/domino/netapp-volumes"):
    print("DFS Project")
    netapp_root = "/domino/netapp-volumes"
elif os.path.exists("/mnt/netapp-volumes"):
    print("Git-Based Project")
    netapp_root = "/mnt/netapp-volumes"

Permissions

  • Owners/Editors: Read-write access to the live volume
  • Readers: Read-only access

Example: Reading Data

import pandas as pd

# Git-Based Project
df = pd.read_parquet("/mnt/netapp-volumes/large-training-data/features.parquet")

# DFS Project
df = pd.read_parquet("/domino/netapp-volumes/large-training-data/features.parquet")

# Read from a specific snapshot tag
df = pd.read_parquet("/mnt/netapp-volumes/snapshot-tags/large-training-data/v2.0/features.parquet")

Example: Writing Data

# Write directly to the live volume
df.to_parquet("/mnt/netapp-volumes/large-training-data/processed/output.parquet", index=False)

# List files
import os
files = os.listdir("/mnt/netapp-volumes/large-training-data/")

Snapshots and Versioning

What is a Snapshot?

A snapshot is a read-only, immutable record of the volume's data at a specific point in time. NetApp snapshots use redirect-on-write — no additional storage is consumed at creation time.

Advantages over Domino Dataset snapshots:

  • Near-instant for any volume size (~3 seconds)
  • Support commit messages for documentation and audit trails
  • No storage overhead at creation time

Create a Snapshot via UI

  1. Go to project → Data > NetApp Volumes
  2. Select the volume → click Take Snapshot
  3. Add an optional commit message (e.g., "Added Q4 customer records")
  4. Add an optional tag name (e.g., v2.0, production, 2024-Q4)
  5. Click Confirm

Create a Snapshot via REST API

# Create snapshot with description and one or more tags (tagNames is an array)
response = requests.post(
    f"{remotefs_url}/remotefs/v1/snapshots",
    headers=headers,
    json={
        "volumeId": "<volume-id>",
        "description": "Added Q4 customer records — 50M new rows",
        "tagNames": ["v2.0"]
    }
)
snapshot = response.json()
print(f"Snapshot ID: {snapshot['id']}")

Add a Tag to an Existing Snapshot

requests.post(
    f"{remotefs_url}/remotefs/v1/snapshots/{snapshot_id}/tags",
    headers=headers,
    json={"name": "production"}
)

Create Snapshot After a Job Run

# Snapshot tied to a specific Domino run for reproducibility
requests.post(
    f"{remotefs_url}/remotefs/v1/rpc/create-snapshot-from-run",
    headers=headers,
    json={
        "volumeId": "<volume-id>",
        "runId": "<domino-run-id>",
        "userId": "<user-id>",
        "description": "post-training-run-42"
    }
)

Restore a Snapshot

requests.post(
    f"{remotefs_url}/remotefs/v1/rpc/restore-snapshot",
    headers=headers,
    json={"snapshotId": "<snapshot-id>"}
)

Roles and Permissions

RoleAPI ValueCapabilities
ReaderVolumeReaderView files and snapshots; mount as read-only
EditorVolumeEditorAll Reader capabilities + modify description, create/delete snapshots, manage shared access, manage users
OwnerVolumeOwnerAll Editor capabilities + update volume grants, request deletion

Update Permissions via UI

  1. Go to Data > NetApp Volumes → select volume
  2. Click the three-dot menu → Edit permissions
  3. Add/remove users and assign roles

Update Permissions via REST API

# targetRole values: "VolumeOwner", "VolumeEditor", "VolumeReader"
requests.put(
    f"{remotefs_url}/remotefs/v1/volumes/{volume_id}/grants",
    headers=headers,
    json=[
        {"targetId": "<user-id>", "targetRole": "VolumeEditor"},
        {"targetId": "<other-user-id>", "targetRole": "VolumeReader"}
    ]
)

Using NetApp Volumes in Jobs

Via Domino REST API

requests.post(
    f"{api_url}/api/jobs/v1/jobs",
    headers=headers,
    json={
        "projectId": "<project-id>",
        "runCommand": "python train.py",
        "hardwareTierId": "<hardware-tier-id>",
        "environmentId": "<environment-id>",  # required — use DOMINO_ENVIRONMENT_ID env var
        "netAppVolumeIds": ["<volume-id>"],
        "snapshotNetAppVolumesOnCompletion": True  # auto-snapshot mounted volumes when job finishes
    }
)

Set snapshotNetAppVolumesOnCompletion: true to automatically take a snapshot of all mounted NetApp volumes when the job completes. This is the recommended approach for training jobs — it captures the exact state of the volume at the end of the run without requiring a separate API call.


Listing Volumes and Snapshots

# List all volumes accessible to you
volumes = requests.get(
    f"{remotefs_url}/remotefs/v1/volumes",
    headers=headers
).json()

for v in volumes["data"]:
    capacity_tb = v["capacity"] / 1_000_000_000_000
    print(f"{v['name']} — {capacity_tb:.1f} TB — ID: {v['id']}")

# List snapshots for a volume
snapshots = requests.get(
    f"{remotefs_url}/remotefs/v1/snapshots",
    headers=headers,
    params={"volumeId": "<volume-id>"}
).json()

for s in snapshots["data"]:
    print(f"Snapshot {s['id']} v{s['version']} — {s.get('description', '')} — tags: {[t['name'] for t in s.get('tags', [])]}")

Best Practices

1. Use Appropriate Storage

Data TypeStorage
Large data of any file type where the entire filesystem should be versioned as a unit, with intentional snapshotsNetApp Volume
Small output files — charts, reports, model binariesArtifacts / DFS (auto-versioned per file, not suitable for large files)
CodeGit / Project files

2. Snapshot Before Changes

# Always snapshot before modifying large volumes
requests.post(
    f"{remotefs_url}/remotefs/v1/snapshots",
    headers=headers,
    json={
        "volumeId": "<volume-id>",
        "description": "Pre-processing baseline snapshot",
        "tagNames": ["pre-processing-2024-01"]
    }
)

# Then run your data transformation
process_data()

3. Use Efficient File Formats

# Parquet for tabular data (faster reads, smaller storage)
df.to_parquet("/mnt/netapp-volumes/dataset/data.parquet")

# Feather for fast pandas I/O
df.to_feather("/mnt/netapp-volumes/dataset/data.feather")

# HDF5 for large numerical arrays
import h5py
with h5py.File("/mnt/netapp-volumes/dataset/arrays.h5", "w") as f:
    f.create_dataset("features", data=features_array)

4. Organize Data

/mnt/netapp-volumes/my-volume/
├── raw/
│   ├── 2024-Q1/
│   └── 2024-Q2/
├── processed/
│   ├── features.parquet
│   └── labels.parquet
└── metadata/
    └── schema.json

5. Use Snapshot Tags for Reproducibility

Reference snapshot tags (not snapshot IDs) in your training scripts so that tagged paths remain stable across runs:

# Reproducible reference using a tag
TRAINING_DATA = "/mnt/netapp-volumes/snapshot-tags/dataset/v2.0/"
df = pd.read_parquet(f"{TRAINING_DATA}/features.parquet")

6. Reading Large Volumes Efficiently

import dask.dataframe as dd

# Lazy read — no data loaded until .compute()
df = dd.read_parquet("/mnt/netapp-volumes/dataset/large_data.parquet")
result = df.groupby("category").mean().compute()

Troubleshooting

Volume Not Found at Mount Path

  • Verify the volume is added to the project (Data > NetApp Volumes)
  • Confirm the volume name matches the path exactly (case-sensitive)
  • Check that the workspace/job was started after the volume was added to the project
  • Restart the workspace to pick up newly added volumes

Permission Denied

  • Check your role on the volume (need Editor or Owner to write)
  • Verify you have been granted access by the volume owner
  • For read-only snapshots, use the snapshot tag path instead of the live volume path

Snapshot Tag Not Mounted

  • Each snapshot has at most one tag path — the most recently applied tag. Earlier tags on the same snapshot are never surfaced as directories and cannot be accessed by path.
  • Tag paths for new snapshots do not appear in a running workspace. The numbered snapshot directory (/snapshots/<name>/<number>/) appears immediately, but the tag symlink path only becomes visible after restarting the workspace.
  • Verify the tag was created successfully via the UI or API

No NetApp Filesystems Available

  • Contact your Domino administrator — they must register NetApp ONTAP filesystems before volumes can be created
  • Admins can register filesystems via Admin > NetApp Volumes

Slow Read Performance

  • Use columnar formats (Parquet, Feather) instead of CSV for tabular data
  • Read only needed columns: pd.read_parquet(path, columns=["col1", "col2"])
  • Use Dask or chunked reading for files larger than available RAM

Documentation Reference

Before writing or verifying any API call, use the cluster swagger to confirm current endpoint paths and field names. Use public docs for workflow context and field explanations.

Get the cluster base URL: $DOMINO_API_HOST (injected by Domino into every workspace, job, and app).

Fetch the NetApp Volumes swagger spec (requires bearer token):

TOKEN=$(curl -s http://localhost:8899/access-token)
# The swagger UI is only accessible via the external cluster URL (not $DOMINO_API_HOST).
# Derive it from the JWT iss claim — works in any workspace type.
CLUSTER_URL=$(echo $TOKEN | cut -d'.' -f2 | python3 -c "
import sys, base64, json, re
p = sys.stdin.read().strip()
p += '=' * (-len(p) % 4)
print(re.sub(r'/auth/realms/.*', '', json.loads(base64.b64decode(p))['iss']))
")
curl -H "Authorization: Bearer $TOKEN" "$CLUSTER_URL/domino-netapp-volumes/swagger/doc.json"
# Browser UI (must be logged in): $CLUSTER_URL/domino-netapp-volumes/swagger/index.html

Public docs (workflow context and field explanations):

  • NetApp Volumes REST API Reference
  • Work with NetApp Volumes
  • Create NetApp Volumes
  • Add or Remove NetApp Volumes on Projects
  • View and Edit NetApp Volumes
  • Version Data with Snapshots
  • Access Data in Domino (comparison guide)
Repository
dominodatalab/domino-claude-plugin
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.