Docs/ Infrastructure/ Storage & Volumes

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
Note
sh0 automatically creates volumes for database services (PostgreSQL, MySQL, Redis, MongoDB). You only need to manually create volumes for custom application storage.

Creating Persistent Volumes

You can create volumes through the dashboard or the API.

Via the Dashboard:

  1. Navigate to your app's Settings → Storage tab.
  2. Click Add Volume.
  3. Enter a volume name (e.g., uploads) and the container mount path.
  4. Optionally set a size limit.
  5. Click Create.
Creating a new persistent volume in the dashboard

Via the API:

Terminal
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.

docker-compose.yml
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.

Volume mount configuration showing container path mapping
Warning
Avoid mounting volumes to paths that contain application code (e.g., /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:

Host Path
/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.

Tip
If you encounter permission errors, check the UID of your application inside the container with 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.

Terminal
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": "..."
  }'
S3 storage provider configuration form

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:

Terminal
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": "..."
  }'
Cloudflare R2 storage provider setup

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.
Storage management panel showing volumes and their usage
Danger
Deleting a volume permanently destroys all data it contains. This action cannot be undone. Always create a backup before deleting a volume.