---
title: "Hosting Media in Payload CMS with Hetzner S3 Object Storage - JHB Software Articles"
description: "In this post, we’ll take an in-depth look at how to use Hetzner Object Storage to host media files in Payload CMS, with a focus on client-side uploads."
---

![Portrait of a young man with short brown hair and blue shirt in front of a green, lush landscape.](https://nbg1.your-objectstorage.com/jhb-software/jens-becker-768x768.jpg)

\\ Written by

[Jens Becker](/en/authors/jens-becker)

\\ Published at

May 30, 2025

\\ Last updated on

June 22, 2025

\\ Tags

\\ Payload CMS

In this post, we’ll take an in-depth look at how to use [Hetzner Object Storage](https://www.hetzner.com/de/storage/object-storage/) to host media files in [Payload CMS](https://payloadcms.com), leveraging its flexible [storage adapter system](https://payloadcms.com/docs/upload/storage-adapters).

Whether you're building a custom CMS or a production-ready platform with asset management capabilities, integrating Hetzner's cost-effective S3-compatible storage with Payload is a powerful and affordable solution.

## Setting Up the Storage Bucket

1.  Log in to the [Hetzner Cloud Console](https://console.hetzner.cloud/projects).
2.  Create a new project (or select an existing one).
3.  Add a new storage bucket within the project.
4.  Navigate to the _Access Keys_ section and generate a new key pair.
5.  Copy the **Access Key ID** and **Secret Access Key** into your Payload project’s `.env` file.
6.  Also, copy the name of your storage bucket.

Your `.env` file should include the following variables:

\\ ts

```
HETZNER_BUCKET="bucket-name"
HETZNER_ACCESS_KEY_ID="access-key-id"
HETZNER_SECRET_ACCESS_KEY="secret-access-key"
```

## Payload Configuration

To connect Payload CMS to Hetzner Object Storage, you’ll use the [`@joneslloyd/payload-storage-hetzner`](https://www.npmjs.com/package/@joneslloyd/payload-storage-hetzner) plugin. This community-built adapter allows seamless integration with Hetzner's S3 API.

Here’s a basic plugin configuration in your Payload setup:

\\ ts

```
import hetznerStorage from '@joneslloyd/payload-storage-hetzner';

export default buildConfig({
  /* your config */
  plugins: [
    hetznerStorage({
      collections: {
        media: true,
      },
      bucket: process.env.HETZNER_BUCKET!,
      region: 'nbg1',
      credentials: {
        accessKeyId: process.env.HETZNER_ACCESS_KEY_ID!,
        secretAccessKey: process.env.HETZNER_SECRET_ACCESS_KEY!,
      },
      acl: 'public-read',
    }),
  ],
});
```

> For full configuration options, refer to the [npm documentation](https://www.npmjs.com/package/@joneslloyd/payload-storage-hetzner).

## Supporting Client-Side Uploads (Important for Vercel)

When deploying Payload on [Vercel](https://vercel.com), you may run into upload limitations due to Vercel's [4.5 MB body size limit](https://vercel.com/guides/how-to-bypass-vercel-body-size-limit-serverless-functions).

To work around this, you can enable **client-side uploads**. This allows media files to be uploaded directly to Hetzner from the browser, bypassing Payload’s API layer.

To enable client uploads, set `clientUploads: true` in the plugin config.

### CORS Configuration for Hetzner

Client-side uploads require properly configured **CORS (Cross-Origin Resource Sharing)** rules on your Hetzner Object Storage bucket. If not configured, you’ll encounter this common error:

> `Access to fetch at '<hetzner-url>' from origin '<your-domain>' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.`

#### Step-by-Step: Setting CORS via AWS CLI

Unfortunately, Hetzner does not currently provide a UI to edit CORS settings. Instead, we’ll use the AWS CLI to configure them.

**1\. Add a CORS file**

Create a `s3-cors.json` file in your Payload project:

\\ ts

```
{
  "CORSRules": [
    {
      "AllowedOrigins": ["https://your-domain.com"],
      "AllowedHeaders": ["*"],
      "AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
      "MaxAgeSeconds": 3000
    }
  ]
}
```

> This allows you to track your CORS configuration in version control.

**2\. Install AWS CLI**  
Follow the [AWS CLI Installation Guide](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html).

**3\. Create AWS CLI Profile**  
In your system’s `.aws` folder (e.g., `~/.aws` or `C:\Users\<your-user>\.aws`), add the following:  
  
`credentials`  

\\ ts

```
[<bucket-name>]
aws_access_key_id = <access-key-id>
aws_secret_access_key = <secret-access-key>
```

`config`  

\\ ts

```
[profile <bucket-name>]
region = eu-central-1
output = json
endpoint_url = https://<bucket-region>.your-objectstorage.com
```

  
**4\. Apply the CORS Configuration**  
From your Payload project directory, run:  

\\ ts

```
aws s3api put-bucket-cors \
  --bucket <your-bucket-name> \
  --cors-configuration file://./s3-cors.json \
  --endpoint-url https://<bucket-region>.your-objectstorage.com \
  --profile <bucket-name>
```

## Conclusion

With this setup, your Payload CMS is now fully integrated with Hetzner's Object Storage — and ready for efficient, scalable media handling. Whether you're building a marketing site, internal dashboard, or custom CMS, this combo offers performance and flexibility without high costs.

Thanks for reading! If you have questions, feedback, or improvements, feel free to reach out.