Mount CoreWeave Object Storage buckets in your marimo notebooks using cw:// URIs.
cwicCLI authenticated to your CoreWeave organizations3cmdinstalled (used by cwic for bucket operations)- kubectl-marimo plugin installed
- marimo-operator running in your cluster
Before creating buckets or tokens, you need an organization-level access policy that grants S3 permissions.
# Create policy JSON file
cat > cw-policy.json << 'EOF'
{
"name": "marimo-s3-policy",
"version": "v1alpha1",
"statements": [
{
"name": "marimo-bucket-access",
"effect": "Allow",
"actions": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"resources": ["*"],
"principals": []
}
]
}
EOF
# Create the policy
cwic cwobject policy create -f cw-policy.jsonNote: You'll add principals (token identities) to this policy after creating tokens.
Create your bucket in the appropriate region:
cwic cwobject mb my-notebook-dataNote: If you get
AccessDenied, ensure your policy includess3:CreateBucketor create the bucket via the CoreWeave console.
# Create a permanent token (or use --duration 86400 for 24h)
cwic cwobject token create --name marimo-s3 --duration PermanentSave the output:
Access Key ID: CWXXXXXXXXXX
Secret Key: cwXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Principal Name: coreweave/uXXXXXXXXXXXXXXXXXXXXX
Update your policy to include the token's principal:
# Get current policy
cwic cwobject policy get
# Update policy JSON to add principal, then re-apply
cwic cwobject policy create -f cw-policy.jsoncat > ~/.s3cfg << EOF
[default]
access_key = CWXXXXXXXXXX
secret_key = cwXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
host_base = cwobject.com
host_bucket = %(bucket)s.cwobject.com
use_https = True
EOF
# Verify access
s3cmd ls s3://my-notebook-data/The kubectl-marimo plugin automatically creates the cw-credentials secret from your ~/.s3cfg when you use cw:// mounts:
# This auto-creates the secret if needed
kubectl marimo edit --source=cw://my-bucket notebook.pyTo create manually:
kubectl create secret generic cw-credentials \
--from-literal=AWS_ACCESS_KEY_ID=CWXXXXXXXXXX \
--from-literal=AWS_SECRET_ACCESS_KEY=cwXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXSecrets are namespace-scoped in Kubernetes. For multi-tenant clusters:
- Each team gets their own namespace
- Create
cw-credentialsin each namespace with team-specific S3 tokens - The operator automatically uses the secret from the MarimoNotebook's namespace
Example setup for two teams:
# Team Alpha - their own S3 token
kubectl create secret generic cw-credentials -n team-alpha \
--from-literal=AWS_ACCESS_KEY_ID=TEAM_ALPHA_KEY \
--from-literal=AWS_SECRET_ACCESS_KEY=TEAM_ALPHA_SECRET
# Team Beta - different S3 token
kubectl create secret generic cw-credentials -n team-beta \
--from-literal=AWS_ACCESS_KEY_ID=TEAM_BETA_KEY \
--from-literal=AWS_SECRET_ACCESS_KEY=TEAM_BETA_SECRETFor RBAC lockdown, limit each team's Role to only access secrets in their namespace. The plugin prompts for confirmation in interactive terminals before creating secrets; in CI/CD (non-TTY) it creates automatically.
Once created, the cw-credentials secret persists in the namespace until explicitly deleted. The plugin will reuse an existing secret without prompting.
To update credentials (e.g., after rotating tokens):
# Delete the existing secret
kubectl delete secret cw-credentials -n <namespace>
# The plugin will prompt to create a new one on next deploy
kubectl marimo edit --source=cw://bucket notebook.pyOr replace directly:
kubectl delete secret cw-credentials -n <namespace>
kubectl create secret generic cw-credentials -n <namespace> \
--from-literal=AWS_ACCESS_KEY_ID=NEW_KEY \
--from-literal=AWS_SECRET_ACCESS_KEY=NEW_SECRETThe plugin reads credentials from ~/.s3cfg, trying sections in order:
[namespace]- if deploying to a specific namespace (e.g.,[team-alpha])[marimo]- marimo-specific credentials[default]- standard s3cmd credentials
This allows namespace-specific or marimo-specific credentials:
[default]
access_key = GENERAL_KEY
secret_key = GENERAL_SECRET
[marimo]
access_key = MARIMO_KEY
secret_key = MARIMO_SECRET
[team-alpha]
access_key = TEAM_ALPHA_KEY
secret_key = TEAM_ALPHA_SECRETWhen deploying with kubectl marimo edit -n team-alpha ..., the plugin will use [team-alpha] credentials if present.
cw://bucket[/path][:mount_point]
| Example | Description |
|---|---|
cw://mybucket |
Mount bucket root |
cw://mybucket/data |
Mount /data subdirectory |
cw://mybucket/data:/mnt/s3 |
Custom mount point |
kubectl marimo edit notebook.py --source=cw://my-notebook-data# /// script
# dependencies = ["marimo"]
# [tool.marimo.k8s]
# mounts = ["cw://my-notebook-data"]
# ///Files are mounted at /home/marimo/notebooks/mounts/cw-0/:
import os
mount_path = "/home/marimo/notebooks/mounts/cw-0"
files = os.listdir(mount_path)
print(f"Files in S3: {files}")
# Read a file
with open(f"{mount_path}/data.csv") as f:
content = f.read()The default endpoint is https://cwobject.com which works from all nodes.
Override with the S3_ENDPOINT environment variable in the operator deployment:
# For LOTA-optimized access from GPU nodes (optional)
kubectl set env deployment/marimo-operator-controller-manager \
-n marimo-operator-system \
S3_ENDPOINT=http://cwlota.com| Endpoint | Use Case |
|---|---|
https://cwobject.com |
Default, works everywhere |
http://cwlota.com |
LOTA-optimized (GPU nodes only) |
The cw:// mount creates an s3fs sidecar container that:
- Runs alongside your marimo container
- Uses FUSE to mount the S3 bucket as a filesystem
- Shares the mount via a shared volume
Features:
- Full read/write support
- Symlink support (unlike mountpoint-s3)
- In-place file editing
- Verify token is active:
cwic cwobject token get --name marimo-s3 - Check policy includes your principal:
cwic cwobject policy get - Ensure policy has required actions for your operation
- Check bucket exists:
s3cmd ls s3://bucket-name/ - Verify bucket region matches your cluster
- Check sidecar logs:
kubectl logs <pod> -c cw-0 - Verify secret exists:
kubectl get secret cw-credentials - Check credentials are correct in secret
The s3fs sidecar requires privileged mode to access /dev/fuse. Ensure you're using operator version with the privileged security context fix.