Skip to content

Commit 539ac85

Browse files
committed
Implement Hetzner Object Storage for image uploads
- Add aws-sdk-s3 gem for S3-compatible storage - Configure Hetzner storage in config/storage.yml - Update development and production to use Hetzner storage - Add environment variables for AWS S3 API compatibility - Create .env.example and development setup files - Add comprehensive Hetzner setup documentation - Support for poster image uploads to cloud storage
1 parent d53c05b commit 539ac85

File tree

7 files changed

+116
-11
lines changed

7 files changed

+116
-11
lines changed

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ gem "omniauth-rails_csrf_protection"
2828
# Environment variables
2929
gem "dotenv-rails"
3030

31+
# AWS S3 for Active Storage (Hetzner object storage)
32+
gem "aws-sdk-s3", require: false
33+
3134
# PDF generation and QR codes
3235
gem "hexapdf"
3336
gem "rqrcode"

Gemfile.lock

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,24 @@ GEM
7575
addressable (2.8.7)
7676
public_suffix (>= 2.0.2, < 7.0)
7777
ast (2.4.3)
78+
aws-eventstream (1.4.0)
79+
aws-partitions (1.1112.0)
80+
aws-sdk-core (3.225.1)
81+
aws-eventstream (~> 1, >= 1.3.0)
82+
aws-partitions (~> 1, >= 1.992.0)
83+
aws-sigv4 (~> 1.9)
84+
base64
85+
jmespath (~> 1, >= 1.6.1)
86+
logger
87+
aws-sdk-kms (1.103.0)
88+
aws-sdk-core (~> 3, >= 3.225.0)
89+
aws-sigv4 (~> 1.5)
90+
aws-sdk-s3 (1.189.0)
91+
aws-sdk-core (~> 3, >= 3.225.0)
92+
aws-sdk-kms (~> 1)
93+
aws-sigv4 (~> 1.5)
94+
aws-sigv4 (1.12.0)
95+
aws-eventstream (~> 1, >= 1.0.2)
7896
base64 (0.3.0)
7997
bcrypt_pbkdf (1.1.1)
8098
benchmark (0.4.1)
@@ -157,6 +175,7 @@ GEM
157175
jbuilder (2.13.0)
158176
actionview (>= 5.0.0)
159177
activesupport (>= 5.0.0)
178+
jmespath (1.6.2)
160179
json (2.12.2)
161180
jwt (2.10.1)
162181
base64
@@ -444,6 +463,7 @@ PLATFORMS
444463
x86_64-linux
445464

446465
DEPENDENCIES
466+
aws-sdk-s3
447467
bootsnap
448468
brakeman
449469
capybara

HETZNER_SETUP.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Hetzner Object Storage Setup
2+
3+
This app uses Hetzner Object Storage (S3-compatible) for image uploads.
4+
5+
## Setup Instructions
6+
7+
### 1. Create Object Storage in Hetzner Cloud Console
8+
9+
1. Go to [Hetzner Cloud Console](https://console.hetzner.cloud/)
10+
2. Navigate to "Object Storage"
11+
3. Create a new Object Storage instance
12+
4. Choose region (recommend `eu-central`)
13+
5. Create a bucket (e.g., `pyramid-scheme-uploads`)
14+
15+
### 2. Get Access Credentials
16+
17+
1. In Object Storage settings, go to "Access Keys"
18+
2. Create a new access key
19+
3. Save the Access Key ID and Secret Access Key
20+
21+
### 3. Configure Environment Variables
22+
23+
Copy `.env.example` to `.env` and fill in your values:
24+
25+
```bash
26+
# Your Hetzner Object Storage credentials
27+
AWS_ACCESS_KEY_ID=your_hetzner_access_key
28+
AWS_SECRET_ACCESS_KEY=your_hetzner_secret_key
29+
AWS_REGION=eu-central
30+
AWS_BUCKET=your-bucket-name
31+
AWS_ENDPOINT=https://your-project.hetzner.eu-central.objects.s3.cloud
32+
```
33+
34+
### 4. Find Your Endpoint URL
35+
36+
Your endpoint URL format: `https://{your-project-id}.{region}.objects.s3.cloud`
37+
38+
Example: `https://12345678.eu-central.objects.s3.cloud`
39+
40+
You can find this in the Hetzner Cloud Console under Object Storage details.
41+
42+
## Testing
43+
44+
Once configured, poster uploads will automatically use Hetzner Object Storage in both development and production environments.
45+
46+
## Bucket Permissions
47+
48+
The bucket should allow:
49+
- `GetObject` - To display uploaded images
50+
- `PutObject` - To upload new images
51+
- `DeleteObject` - To delete images (if needed)
52+
53+
Hetzner buckets are private by default, which is perfect for this use case.
54+
55+
## CORS Configuration (if needed)
56+
57+
If you need direct browser uploads in the future, configure CORS:
58+
59+
```xml
60+
<CORSConfiguration>
61+
<CORSRule>
62+
<AllowedOrigin>https://your-domain.com</AllowedOrigin>
63+
<AllowedMethod>GET</AllowedMethod>
64+
<AllowedMethod>POST</AllowedMethod>
65+
<AllowedMethod>PUT</AllowedMethod>
66+
<AllowedHeader>*</AllowedHeader>
67+
</CORSRule>
68+
</CORSConfiguration>
69+
```

PRODUCTION_ENV.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,17 @@ SECRET_KEY_BASE=your_long_random_secret
2222
RAILS_MAX_THREADS=5
2323
```
2424

25+
## Hetzner Object Storage (S3 Compatible)
26+
27+
```bash
28+
# Hetzner Object Storage credentials
29+
AWS_ACCESS_KEY_ID=your_hetzner_access_key
30+
AWS_SECRET_ACCESS_KEY=your_hetzner_secret_key
31+
AWS_REGION=eu-central
32+
AWS_BUCKET=your-bucket-name
33+
AWS_ENDPOINT=https://your-project.hetzner.eu-central.objects.s3.cloud
34+
```
35+
2536
## Application Specific
2637

2738
```bash

config/environments/development.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@
2828
# Change to :null_store to avoid any caching.
2929
config.cache_store = :memory_store
3030

31-
# Store uploaded files on the local file system (see config/storage.yml for options).
32-
config.active_storage.service = :local
31+
# Store uploaded files on Hetzner Object Storage (see config/storage.yml for options).
32+
config.active_storage.service = :hetzner
3333

3434
# Don't care if the mailer can't send.
3535
config.action_mailer.raise_delivery_errors = false

config/environments/production.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
2222
# config.asset_host = "http://assets.example.com"
2323

24-
# Store uploaded files on the local file system (see config/storage.yml for options).
25-
config.active_storage.service = :local
24+
# Store uploaded files on Hetzner Object Storage (see config/storage.yml for options).
25+
config.active_storage.service = :hetzner
2626

2727
# Assume all access to the app is happening through a SSL-terminating reverse proxy.
2828
config.assume_ssl = true

config/storage.yml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ local:
66
service: Disk
77
root: <%= Rails.root.join("storage") %>
88

9-
# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10-
# amazon:
11-
# service: S3
12-
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13-
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14-
# region: us-east-1
15-
# bucket: your_own_bucket-<%= Rails.env %>
9+
# Hetzner Object Storage (S3 compatible)
10+
hetzner:
11+
service: S3
12+
access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
13+
secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>
14+
region: <%= ENV.fetch("AWS_REGION") { "eu-central" } %>
15+
bucket: <%= ENV["AWS_BUCKET"] %>
16+
endpoint: <%= ENV.fetch("AWS_ENDPOINT") { "https://your-project.hetzner.eu-central.objects.s3.cloud" } %>
17+
force_path_style: true
1618

1719
# Remember not to checkin your GCS keyfile to a repository
1820
# google:

0 commit comments

Comments
 (0)