-
Notifications
You must be signed in to change notification settings - Fork 16
Image Storage
FewohBee stores user-uploaded images (room category photos, template images) through a pluggable storage layer. Two backends are supported:
-
local(default) — files are written to the localpublic/resources/images/tree and served by nginx as static assets. Best for single-server installations. -
s3— files are stored in any S3-compatible bucket (Hetzner Object Storage, MinIO, Cloudflare R2, Wasabi, AWS S3, …). Required for Kubernetes / multi-pod deployments where pods must share uploaded content.
The backend is selected via the STORAGE_ADAPTER environment variable and is baked into the compiled Symfony container — changing it requires clearing the cache (bin/console cache:clear).
Existing installations do not need to change anything.
STORAGE_ADAPTERdefaults tolocal, the local adapter targets the samepublic/resources/images/...paths that were used before this setting existed, and generated URLs are identical to the previous behaviour. No file migration is required.
No configuration needed. Make sure the web user (www-data) has write permissions on:
-
public/resources/images/export/— used by the template editor image upload -
public/resources/images/room-categories/— used for room category photos (thumb, medium, original variants)
In .env:
STORAGE_ADAPTER=localUse this for Kubernetes deployments, multi-server installations, or whenever you want to offload images to object storage. The implementation uses the AWS S3 v3 driver, which speaks the standard S3 protocol — it works with any S3-compatible provider, not only AWS.
-
Create a bucket in your Hetzner Cloud project (Object Storage → Buckets). Choose a location (
fsn1,nbg1,hel1). -
Generate S3 credentials (Access Key + Secret Key) in the Hetzner Cloud Console.
-
Set the bucket's permissions to public read so that browsers can load image URLs directly without signed requests.
-
Configure CORS on the bucket if the booking page is served from a different origin than the bucket URL. A minimal policy that allows
GETfrom any origin:<CORSConfiguration> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>
STORAGE_ADAPTER=s3
STORAGE_S3_ENDPOINT=https://fsn1.your-objectstorage.com
STORAGE_S3_REGION=fsn1
STORAGE_S3_BUCKET=fewohbee-images
STORAGE_S3_KEY=your-access-key
STORAGE_S3_SECRET=your-secret-key
STORAGE_S3_PUBLIC_URL=https://fewohbee-images.fsn1.your-objectstorage.com-
STORAGE_S3_ENDPOINT— the provider's regional S3 endpoint. For Hetzner it ishttps://<region>.your-objectstorage.com. -
STORAGE_S3_REGION— provider region identifier (Hetzner:fsn1,nbg1, orhel1; MinIO/R2: any value, oftenus-east-1). -
STORAGE_S3_BUCKET— bucket name. -
STORAGE_S3_KEY/STORAGE_S3_SECRET— access credentials. -
STORAGE_S3_PUBLIC_URL— the public base URL of the bucket. This is the prefix used when building image<img src="...">URLs for the browser. For Hetzner this is typicallyhttps://<bucket>.<region>.your-objectstorage.com.
After updating .env, clear the Symfony cache (only required in prod mode; dev mode picks the change up on the next request):
bin/console cache:clearDocker setup users: when changing storage settings on a running container, restart the
phpservice so the new environment is applied:docker compose restart php
When switching an existing installation from local to s3, copy the contents of the local image directories into the bucket. FewohBee ships a console command for this:
bin/console app:storage:migrate-imagesThe command iterates public/resources/images/export/ and public/resources/images/room-categories/ and uploads every file into the currently configured storages (STORAGE_ADAPTER=s3 must already be active). Files that already exist at the destination are skipped, so the command is idempotent and safe to re-run.
-
--dry-run— list what would be copied without writing anything. Useful as a sanity check. -
--overwrite— overwrite files that already exist at the destination. Use this only if you want to refresh the bucket from local files.
-
Set up the bucket and credentials as described above.
-
Edit
.env: setSTORAGE_ADAPTER=s3plus allSTORAGE_S3_*values. -
Clear the cache:
bin/console cache:clear. -
Verify the configuration with a dry run:
bin/console app:storage:migrate-images --dry-run
-
Run the actual migration:
bin/console app:storage:migrate-images
-
Open the public booking page and a room category in the settings to confirm that images are now served from the bucket URL (visible in the browser's network tab).
-
Once you have verified everything works, the local
public/resources/images/directories can be backed up and removed — they are no longer used.
The local directories remain on disk until you delete them, so a rollback to STORAGE_ADAPTER=local is always possible by reverting the .env change.
-
Local mode — URLs are root-relative paths like
/resources/images/room-categories/42/medium_photo.jpg. The application's base path is prepended automatically, so the same URL works for installations running at/or in a sub-directory (e.g./fewohbee). -
S3 mode — URLs are absolute, pointing directly at the bucket:
https://fewohbee-images.fsn1.your-objectstorage.com/room-categories/42/medium_photo.jpg. Browsers load these directly from object storage, no application traffic is involved.
All URL generation goes through a central ImageUrlGenerator service, so both the backend (room category settings, template editor) and the public booking page produce consistent URLs without any caller-side string concatenation.