> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Storage Drivers

> Configuration guide for fs and S3 drivers

Sonamu supports multiple storage drivers based on **Flydrive**. You can use the local file system (fs) and AWS S3 with the same interface.

## Supported Drivers

<CardGroup cols={2}>
  <Card title="fs (File System)" icon="folder">
    Local file system
  </Card>

  <Card title="s3 (AWS S3)" icon="cloud">
    AWS S3 compatible storage
  </Card>
</CardGroup>

## fs Driver

Stores files in the **local file system**.

### Basic Configuration

```typescript theme={null}
import { drivers, type SonamuConfig } from "sonamu";

export const config: SonamuConfig = {
  server: {
    storage: {
      default: 'fs',
      drivers: {
        fs: drivers.fs({
          location: './uploads',  // File storage location
          urlBuilder: {
            generateURL: (key) => `/uploads/${key}`,
          }
        }),
      }
    }
  }
};
```

### Configuration Options

<Tabs>
  <Tab title="location" icon="folder">
    **File storage location**

    ```typescript theme={null}
    fs: drivers.fs({
      location: './uploads',  // Relative path
    })

    fs: drivers.fs({
      location: '/var/app/uploads',  // Absolute path
    })
    ```

    **Recommended**: Relative path from project root
  </Tab>

  <Tab title="urlBuilder" icon="link">
    **URL generation rules**

    ```typescript theme={null}
    fs: drivers.fs({
      location: './uploads',
      urlBuilder: {
        generateURL: (key) => `/uploads/${key}`,
      }
    })
    ```

    **Required**: fs driver requires urlBuilder configuration

    **Example**:

    ```typescript theme={null}
    // key: 'avatars/user-1.png'
    // URL: '/uploads/avatars/user-1.png'
    ```
  </Tab>

  <Tab title="visibility" icon="eye">
    **File access permissions** (Unix permissions)

    ```typescript theme={null}
    fs: drivers.fs({
      location: './uploads',
      visibility: 'public',  // or 'private'
      urlBuilder: {
        generateURL: (key) => `/uploads/${key}`,
      }
    })
    ```

    **Default**: `public` (0644)

    * `public`: Readable by all users
    * `private`: Readable only by owner (0600)
  </Tab>
</Tabs>

### Practical Examples

#### Development Environment

```typescript theme={null}
export const config: SonamuConfig = {
  server: {
    storage: {
      default: 'fs',
      drivers: {
        fs: drivers.fs({
          location: './uploads',
          urlBuilder: {
            generateURL: (key) => `/uploads/${key}`,
          }
        }),
      }
    }
  }
};
```

**Usage**:

```typescript theme={null}
await disk.put('avatars/user-1.png', buffer);
// File location: ./uploads/avatars/user-1.png
// URL: /uploads/avatars/user-1.png
```

#### Static File Serving

You need to serve static files with Fastify:

```typescript theme={null}
// sonamu.config.ts
import path from "path";

export const config: SonamuConfig = {
  server: {
    plugins: {
      static: {
        root: path.join(process.cwd(), 'uploads'),
        prefix: '/uploads/',
      }
    },
    storage: {
      default: 'fs',
      drivers: {
        fs: drivers.fs({
          location: './uploads',
          urlBuilder: {
            generateURL: (key) => `/uploads/${key}`,
          }
        }),
      }
    }
  }
};
```

**Result**:

```
http://localhost:3000/uploads/avatars/user-1.png
→ Serves ./uploads/avatars/user-1.png file
```

## s3 Driver

Stores files in **AWS S3** or compatible storage.

### Basic Configuration

```typescript theme={null}
import { drivers, type SonamuConfig } from "sonamu";

export const config: SonamuConfig = {
  server: {
    storage: {
      default: 's3',
      drivers: {
        s3: drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
          },
          region: process.env.AWS_REGION!,
          bucket: process.env.AWS_BUCKET!,
        }),
      }
    }
  }
};
```

### Required Environment Variables

```.env theme={null}
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=ap-northeast-2
AWS_BUCKET=my-app-uploads
```

### Configuration Options

<Tabs>
  <Tab title="credentials" icon="key">
    **AWS credentials**

    ```typescript theme={null}
    s3: drivers.s3({
      credentials: {
        accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
      },
      // ...
    })
    ```

    **How to obtain**:

    1. AWS Console → IAM
    2. Create user
    3. Grant S3 permissions (`AmazonS3FullAccess`)
    4. Create Access Key
  </Tab>

  <Tab title="region" icon="globe">
    **AWS Region**

    ```typescript theme={null}
    s3: drivers.s3({
      region: 'ap-northeast-2',  // Seoul
      // ...
    })
    ```

    **Major regions**:

    * `ap-northeast-2`: Seoul
    * `ap-northeast-1`: Tokyo
    * `us-east-1`: N. Virginia
    * `us-west-2`: Oregon
  </Tab>

  <Tab title="bucket" icon="bucket">
    **S3 bucket name**

    ```typescript theme={null}
    s3: drivers.s3({
      bucket: 'my-app-uploads',
      // ...
    })
    ```

    **Creating a bucket**:

    1. AWS Console → S3
    2. Click "Create bucket"
    3. Enter a unique name
    4. Select region
    5. Configure public access blocking
  </Tab>

  <Tab title="urlBuilder" icon="link">
    **URL generation rules** (optional)

    ```typescript theme={null}
    s3: drivers.s3({
      bucket: 'my-bucket',
      urlBuilder: {
        generateURL: (key) =>
          `https://my-bucket.s3.amazonaws.com/${key}`,
      }
    })
    ```

    **Default**: Uses S3's default URL

    ```
    https://bucket.s3.region.amazonaws.com/key
    ```

    **When using CloudFront**:

    ```typescript theme={null}
    urlBuilder: {
      generateURL: (key) =>
        `https://d111111abcdef8.cloudfront.net/${key}`,
    }
    ```
  </Tab>
</Tabs>

### Practical Examples

#### Basic S3 Configuration

```typescript theme={null}
export const config: SonamuConfig = {
  server: {
    storage: {
      default: 's3',
      drivers: {
        s3: drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
          },
          region: 'ap-northeast-2',
          bucket: 'my-app-uploads',
        }),
      }
    }
  }
};
```

**Usage**:

```typescript theme={null}
await disk.put('avatars/user-1.png', buffer);
// S3 location: s3://my-app-uploads/avatars/user-1.png
// URL: https://my-app-uploads.s3.ap-northeast-2.amazonaws.com/avatars/user-1.png
```

#### CloudFront CDN Integration

```typescript theme={null}
export const config: SonamuConfig = {
  server: {
    storage: {
      default: 's3',
      drivers: {
        s3: drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
          },
          region: 'ap-northeast-2',
          bucket: 'my-app-uploads',
          urlBuilder: {
            generateURL: (key) =>
              `https://${process.env.CLOUDFRONT_DOMAIN}/${key}`,
          }
        }),
      }
    }
  }
};
```

```.env theme={null}
CLOUDFRONT_DOMAIN=d111111abcdef8.cloudfront.net
```

**Benefits**:

* Fast transfer speed (edge locations)
* Reduced bandwidth costs
* Caching

#### Public Bucket Configuration

To make an S3 bucket public:

```json theme={null}
// S3 bucket policy (AWS Console → S3 → Bucket → Permissions → Bucket Policy)
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-app-uploads/*"
    }
  ]
}
```

**Caution**: Use private buckets for sensitive data

## S3 Compatible Storage

### Cloudflare R2

```typescript theme={null}
export const config: SonamuConfig = {
  server: {
    storage: {
      default: 'r2',
      drivers: {
        r2: drivers.s3({
          credentials: {
            accessKeyId: process.env.R2_ACCESS_KEY_ID!,
            secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
          },
          endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
          region: 'auto',
          bucket: process.env.R2_BUCKET!,
          urlBuilder: {
            generateURL: (key) =>
              `https://${process.env.R2_PUBLIC_DOMAIN}/${key}`,
          }
        }),
      }
    }
  }
};
```

**Features**:

* Free egress (no transfer costs)
* S3 API compatible
* Cloudflare network

### MinIO (Self-hosted)

```typescript theme={null}
export const config: SonamuConfig = {
  server: {
    storage: {
      default: 'minio',
      drivers: {
        minio: drivers.s3({
          credentials: {
            accessKeyId: process.env.MINIO_ACCESS_KEY!,
            secretAccessKey: process.env.MINIO_SECRET_KEY!,
          },
          endpoint: 'http://localhost:9000',
          region: 'us-east-1',
          bucket: 'uploads',
          forcePathStyle: true,  // Required for MinIO
        }),
      }
    }
  }
};
```

## Driver Comparison

| Feature     | fs                       | S3                        |
| ----------- | ------------------------ | ------------------------- |
| Cost        | Free (disk space)        | Paid (storage + transfer) |
| Scalability | Limited (server disk)    | Unlimited                 |
| Speed       | Very fast                | Fast (network)            |
| Backup      | Manual                   | Automatic (versioning)    |
| CDN         | Requires separate setup  | CloudFront integration    |
| Use case    | Development, small scale | Production, large scale   |

## Practical Strategies

### Development/Production Separation

```typescript theme={null}
const isDevelopment = process.env.NODE_ENV === 'development';

export const config: SonamuConfig = {
  server: {
    storage: {
      default: isDevelopment ? 'fs' : 's3',
      drivers: {
        // Development: Local
        fs: drivers.fs({
          location: './uploads',
          urlBuilder: {
            generateURL: (key) => `/uploads/${key}`,
          }
        }),

        // Production: S3
        s3: drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
          },
          region: process.env.AWS_REGION!,
          bucket: process.env.AWS_BUCKET!,
        }),
      }
    }
  }
};
```

### Multi-Bucket Strategy

```typescript theme={null}
export const config: SonamuConfig = {
  server: {
    storage: {
      default: 'private',
      drivers: {
        // Private documents
        private: drivers.s3({
          credentials: { /* ... */ },
          region: 'ap-northeast-2',
          bucket: 'my-app-private',
        }),

        // Public files
        public: drivers.s3({
          credentials: { /* ... */ },
          region: 'ap-northeast-2',
          bucket: 'my-app-public',
        }),

        // Backup
        backup: drivers.s3({
          credentials: { /* ... */ },
          region: 'ap-northeast-2',
          bucket: 'my-app-backup',
        }),
      }
    }
  }
};
```

**Usage**:

```typescript theme={null}
// Profile photos → public
await file.saveToDisk('avatars/user-1.png', 'public');

// Private documents → private (default)
await file.saveToDisk('documents/user-1/doc.pdf');

// Backup → backup
await Sonamu.storage.use('backup').put('backup.json', data);
```

## Cautions

<Warning>
  **Cautions when configuring drivers**:

  1. **Environment variable management**: Never hardcode
     ```typescript theme={null}
     // ❌ Hardcoded
     credentials: {
       accessKeyId: 'AKIA...',
       secretAccessKey: '...',
     }

     // ✅ Environment variables
     credentials: {
       accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
       secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
     }
     ```

  2. **fs urlBuilder required**: fs driver requires urlBuilder
     ```typescript theme={null}
     // ❌ No urlBuilder
     fs: drivers.fs({
       location: './uploads',
     })

     // ✅ urlBuilder configured
     fs: drivers.fs({
       location: './uploads',
       urlBuilder: {
         generateURL: (key) => `/uploads/${key}`,
       }
     })
     ```

  3. **S3 permissions check**: Grant S3 permissions to IAM user
     ```json theme={null}
     // Minimum permissions
     {
       "Version": "2012-10-17",
       "Statement": [
         {
           "Effect": "Allow",
           "Action": [
             "s3:PutObject",
             "s3:GetObject",
             "s3:DeleteObject"
           ],
           "Resource": "arn:aws:s3:::my-bucket/*"
         }
       ]
     }
     ```

  4. **Bucket name uniqueness**: S3 bucket names must be globally unique
     ```typescript theme={null}
     // ❌ Name already in use
     bucket: 'uploads'  // Error!

     // ✅ Unique name
     bucket: 'my-app-uploads-prod-2024'
     ```

  5. **Region matching**: bucket and region must match
     ```typescript theme={null}
     // ❌ Region mismatch
     region: 'us-east-1',
     bucket: 'my-bucket'  // Created in ap-northeast-2

     // ✅ Region verified
     region: 'ap-northeast-2',
     bucket: 'my-bucket'  // Created in ap-northeast-2
     ```
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Storage Manager" icon="database" href="/en/advanced-features/storage/storage-manager">
    Managing multiple disks
  </Card>

  <Card title="File Operations" icon="upload" href="/en/advanced-features/storage/file-operations">
    File upload and download
  </Card>

  <Card title="URL Builder" icon="link" href="/en/advanced-features/storage/url-builder">
    Generating file URLs
  </Card>
</CardGroup>
