Logotipo Hedhog

Get Hedhog updates in your inbox

New releases, fresh recipes, and breaking changes — no spam.

File Storage

Hedhog handles file uploads through @hed-hog/core's FileService. All uploaded content goes through a single storage adapter, selected once via an Integration Profile. Swap the provider later without touching application code or re-uploading existing files.

Status: ✅ Fully implemented — Local, S3, GCS, Azure Blob, and S3-compatible providers all work.

Local, S3, GCS, Azure Blob, and S3-compatible providers feeding into FileService.upload()

Quick Setup

  1. Create an Integration Profile (type Storage, pick a provider) at Settings → Integration Profiles
  2. Fill in the provider's config fields (tables below)
  3. Go to Settings → Configurations → File Storage and set Storage Profile to it
  4. Upload a test file anywhere in the admin (e.g. your own avatar) and confirm the returned URL actually loads

How It Works

  1. A file is uploaded through any endpoint that calls FileService.upload()
  2. FileService reads the file-storage-profile-id setting to find the active Integration Profile (type storage)
  3. It maps that profile's provider-specific config JSON into the adapter the matching provider class expects (local disk, S3, GCS, Azure Blob, or an S3-compatible service)
  4. The file is written via the adapter, a record is saved in the file table with the resulting URL/key and metadata, and the public URL is returned to the client

If no storage profile is configured yet, the server still starts normally — it just logs a warning on boot, and any upload attempt fails with No file storage profile configured until you set one. This is the expected state right after a fresh install.

Supported Providers

Providerintegration_provider slugDescription
LocallocalStores files on the server's own filesystem
Amazon S3s3AWS object storage
Google Cloud StoragegcsGCP object storage
Azure Blob Storageazure-blobMicrosoft Azure object storage
S3-Compatibles3-compatibleMinIO, Wasabi, DigitalOcean Spaces, and any other S3-API-compatible service

Setting Up

  1. Go to Settings → Integration ProfilesNew → Type Storage, pick a provider, and fill in its config fields (table below)
  2. Go to Settings → Configurations → File Storage and set the Storage Profile field (file-storage-profile-id) to that profile
  3. Uploads immediately start using the new provider — existing file records and their URLs are not migrated or rewritten, since Hedhog has no way to know whether old files should move

Local

Stores files on the server filesystem. Suitable for development or single-server deployments — not recommended once you run more than one API instance, since uploads written by one instance won't be visible to the others.

Config fieldDescription
base_pathDirectory (relative to the API process, or absolute) where files are written. Defaults to storage if left empty

Make sure the API process has write permission to this directory, and that it survives deploys/restarts (don't point it inside a directory your deploy pipeline wipes).


Amazon S3

Config fieldDescription
access_key_idAWS access key ID
secret_access_keyAWS secret access key
regionAWS region (defaults to us-east-1 if left empty)
bucketS3 bucket name

IAM permissions needed, scoped to the bucket:

{
  "Effect": "Allow",
  "Action": ["s3:PutObject", "s3:GetObject", "s3:DeleteObject"],
  "Resource": "arn:aws:s3:::your-bucket/*"
}

Decide up front whether the bucket serves files publicly or via signed URLs — that's a bucket policy decision made in the S3 console, not a Hedhog setting.


Google Cloud Storage

Config fieldDescription
key_file_jsonThe full contents of a GCP service account JSON key, pasted as a string — not a file path. The API process never reads from disk for this provider
bucketGCS bucket name

Create the service account under IAM & Admin → Service Accounts, grant it the Storage Object Creator and Storage Object Viewer roles, then Keys → Add Key → JSON and paste the downloaded file's contents into key_file_json.


Azure Blob Storage

Config fieldDescription
accountStorage account name
keyStorage account access key
containerBlob container name

Find the account name and key in the Azure portal under Storage account → Access keys. The container must already exist — Hedhog does not create it automatically.


S3-Compatible (MinIO, Wasabi, DigitalOcean Spaces, …)

Use this provider for anything that speaks the S3 API but isn't AWS itself.

Config fieldDescription
access_key_idAccess key for the service
secret_access_keySecret key for the service
regionRegion identifier the service expects (e.g. nyc3 for DigitalOcean Spaces)
bucketBucket / Space name
endpointFull custom endpoint URL, e.g. https://nyc3.digitaloceanspaces.com for Spaces, or https://minio.your-domain.com for a self-hosted MinIO

Without endpoint set, requests go to AWS's own endpoints and will fail against any non-AWS service — this is the single field people most often forget when switching from S3 to a compatible provider.


Verify It Worked

  • Upload a file anywhere in the admin (a user avatar works well) and confirm a new row appears in file
  • Open the returned URL directly in a browser tab — it should load the file, not 404 or hang

Troubleshooting

SymptomLikely cause
No file storage profile configuredfile-storage-profile-id isn't set, or points at a deleted profile
Integration profile X is not a storage profileWrong profile selected — its type isn't storage
Provider <slug> not foundThe file_provider seed row for that provider is missing — usually means a migration wasn't run after adding storage support
Uploads succeed but the URL 404sFor Local storage, the serving web server/proxy isn't pointed at base_path; for S3/GCS/Azure, the bucket/container isn't public and no signed-URL flow is wired up
Old files broke after switching providersExpected — Hedhog never migrates existing files between backends automatically