Storage & Volumes
Persistent storage for your containers, with support for local disk, S3, and Cloudflare R2 as storage backends.
What Are Volumes
By default, container filesystems are ephemeral -- any data written inside a container is lost when the container restarts or redeploys. Volumes solve this by providing persistent storage that survives container lifecycle events.
Common use cases for volumes include:
- Database data directories (PostgreSQL, MySQL, MongoDB)
- User-uploaded files and media
- Application logs that need to persist
- Shared configuration between containers
- Cache directories that should survive restarts
Creating Persistent Volumes
You can create volumes through the dashboard or the API.
Via the Dashboard:
- Navigate to your app's Settings → Storage tab.
- Click Add Volume.
- Enter a volume name (e.g.,
uploads) and the container mount path. - Optionally set a size limit.
- Click Create.
Via the API:
curl -X POST https://your-server:9000/api/apps/my-app/mounts \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "uploads",
"mount_path": "/app/uploads",
"size_limit_mb": 5120
}'Mounting Volumes to Containers
When you create a volume, you specify the path inside the container where it will be mounted. The volume data is stored on the host filesystem and bind-mounted into the container at the specified path.
services:
web:
image: my-app:latest
volumes:
- uploads:/app/uploads
- logs:/var/log/app
volumes:
uploads:
logs:When deploying via Docker Compose, sh0 automatically creates and manages the named volumes defined in your compose file.
/app). This will override your deployed code with whatever is on the volume. Mount volumes to subdirectories like /app/data or /app/uploads instead.Volume Paths & Permissions
sh0 stores volume data on the host at:
/var/lib/sh0/volumes/{stack_id}/{volume_name}Permissions are set to match the container user. If your application runs as a non-root user (which sh0 enforces by default), the volume directory is owned by that user's UID/GID.
id and ensure the volume directory has matching ownership. You can fix permissions from the sh0 terminal.Storage Providers
In addition to local volumes, sh0 supports external storage providers for backups and object storage.
Local Disk
The default storage backend. Volumes are stored directly on the server's filesystem. This is the simplest option and provides the best performance for most workloads.
Amazon S3
Configure an S3 bucket for backups and large file storage. Navigate to Settings → Storage Providers to add your S3 credentials.
curl -X POST https://your-server:9000/api/storage-providers \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "production-backups",
"type": "s3",
"bucket": "my-backups",
"region": "us-east-1",
"access_key_id": "AKIA...",
"secret_access_key": "..."
}'Cloudflare R2
Cloudflare R2 is an S3-compatible object store with zero egress fees. sh0 supports R2 natively -- configure it the same way as S3, using your R2 endpoint:
curl -X POST https://your-server:9000/api/storage-providers \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "r2-backups",
"type": "s3",
"bucket": "my-backups",
"endpoint": "https://ACCOUNT_ID.r2.cloudflarestorage.com",
"access_key_id": "...",
"secret_access_key": "..."
}'Managing Storage
The Storage panel in the dashboard shows all volumes for a stack, their sizes, and which containers they are mounted to. From here you can:
- Browse files: Use the built-in file manager to view volume contents.
- Download files: Export individual files or entire directories.
- Resize: Increase the size limit of a volume.
- Delete: Remove a volume and all its data (requires confirmation).
- Backup: Trigger a manual backup to a configured storage provider.