Schema for storage provider configurations.
Buckets represent storage locations where files can be stored. They can be:
- Local filesystem storage
- AWS S3 buckets
- Backblaze B2 buckets
- Cloudflare R2 buckets
- Tigris buckets
Priority (legacy)
Where new files go is decided by the storage profile, which has an upload
order (write_priority) per bucket. A bucket's own priority is no longer
shown or edited in the admin and is not copied into any profile. It is read
only by the pre-profile selection (Manager.store_file/2 with
:redundancy_copies / :priority_buckets, or a database with no profiles)
and to order the buckets a read falls back to when a file has no recorded
location:
priority = 0(default): the pool, in random order when writingpriority > 0: a fixed position (1 = first, 2 = second, etc.)
Fields
name- Display name for the bucketprovider- Storage provider: "local", "s3", "b2", "r2", "tigris"region- AWS region or equivalent (nullable)endpoint- Custom S3-compatible endpoint (nullable)bucket_name- S3 bucket name (nullable)access_key_id- Credentials identifier (nullable, stored as-is — not a secret)secret_access_key- Encrypted at rest viaPhoenixKit.Integrations.Encryption(nullable); decrypted only at the point of use (e.g.Providers.S3's AWS config builder), never by a general accessor likeStorage.get_bucket/1. A value already in this column when encryption was added is migrated opportunistically, not by a bulk backfill: the changeset re-derives and re-encrypts it on the next save of the bucket for ANY reason (seeencrypt_secret_access_key/1), so it stays plaintext only until thenintegration_uuid- Alternative credential source: aPhoenixKit.Integrationsconnection uuid (nullable, no FK). Mutually exclusive withaccess_key_id/secret_access_key— a changeset may set one source or the other, never bothcdn_url- CDN endpoint for file serving (nullable)access_type- How files are served: "public", "private", "signed" (default: "public")enabled- Whether bucket is activepriority- Legacy selection priority (0 = random); see abovemax_size_mb- Maximum storage capacity in MB (nullable = unlimited)owner_uuid- Whose bucket it is (V206). NULL is the site's, which is every bucket an admin creates. A user's own bucket carries its owner, never appears in a site listing (Storage.list_buckets/0,Storage.list_enabled_buckets/0), cannot belocal, and takes its keys from the owner's personal connection only. Never cast from params: onlyowned_changeset/3sets it, from the signed-in user. No foreign key (seeMigrations.Postgres.V206).
Access Types
public- Redirect to public URL (default, fastest, uses CDN)private- Proxy files through server (for ACL-protected buckets)signed- Redirect to a presigned URL made for each request (5 minutes); proxied when the provider cannot sign. The plain object URL is never handed out
Examples
# Local storage bucket
%Bucket{
name: "Local SSD",
provider: "local",
enabled: true,
priority: 0,
max_size_mb: 512_000 # 500 GB
}
# AWS S3 bucket
%Bucket{
name: "Production S3",
provider: "s3",
region: "us-east-1",
bucket_name: "my-app-files",
access_key_id: "AKIA...",
secret_access_key: "...",
cdn_url: "https://cdn.example.com",
enabled: true,
priority: 1 # Highest priority
}
# Backblaze B2 bucket
%Bucket{
name: "Backup B2",
provider: "b2",
endpoint: "s3.us-west-002.backblazeb2.com",
bucket_name: "my-backup-bucket",
access_key_id: "...",
secret_access_key: "...",
enabled: true,
priority: 2
}
Summary
Functions
Changeset for creating or updating a bucket.
Returns whether this bucket is a cloud storage bucket (S3, B2, R2, Tigris).
The kind of place a bucket is for the copies a storage profile counts:
:local (the server's own disk) or :cloud (S3, B2, R2, Tigris).
Returns whether this bucket is a local storage bucket.
Changeset for a user's own bucket (V206).
Types
@type t() :: %PhoenixKit.Modules.Storage.Bucket{ __meta__: term(), access_key_id: String.t() | nil, access_type: String.t(), bucket_name: String.t() | nil, cdn_url: String.t() | nil, enabled: boolean(), endpoint: String.t() | nil, file_locations: [PhoenixKit.Modules.Storage.FileLocation.t()] | Ecto.Association.NotLoaded.t(), inserted_at: DateTime.t() | nil, integration_uuid: UUIDv7.t() | nil, max_size_mb: integer() | nil, name: String.t(), owner_uuid: UUIDv7.t() | nil, priority: integer(), provider: String.t(), region: String.t() | nil, secret_access_key: String.t() | nil, updated_at: DateTime.t() | nil, uuid: UUIDv7.t() | nil }
Functions
Changeset for creating or updating a bucket.
Required Fields
nameprovider(must be one of: "local", "s3", "b2", "r2", "tigris")
Validation Rules
- Provider must be valid
- Priority must be >= 0
- Quality must be between 1-100 (if provided)
- S3/B2/R2 buckets require credentials — either
access_key_id/secret_access_keydirectly, or anintegration_uuid integration_uuidandaccess_key_id/secret_access_keyare mutually exclusive: setting both in the same change is a validation error, not a silent overwrite of one by the other
secret_access_key is encrypted at rest (see
PhoenixKit.Integrations.Encryption) as part of this changeset —
already-encrypted values pass through unchanged. A row written before
encryption existed stays plaintext until the next save of any kind (no
bulk backfill — see the secret_access_key field doc above).
Returns whether this bucket is a cloud storage bucket (S3, B2, R2, Tigris).
The kind of place a bucket is for the copies a storage profile counts:
:local (the server's own disk) or :cloud (S3, B2, R2, Tigris).
Returns whether this bucket is a local storage bucket.
Changeset for a user's own bucket (V206).
owner_uuid comes from the signed-in user, never from attrs. Only S3
protocol providers (a local bucket would be arbitrary filesystem access on
the server), always on a connection the owner owns (never keys stored on the
bucket), always signed (the plain object URL is never handed out), with no
cdn_url and no priority: the owner's storage profile decides how it is used.
The endpoint is checked under the :personal policy, resolving the host, at
save time; Providers.S3 checks it again whenever a request is built.
:connection_owned? (a function of the connection uuid) says whether the
owner owns the connection; Storage passes the real check.