Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/docs/content/guides/storage/uploads/file-limits.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ sidebar_label: 'Limits'

## Global file size

You can set the max file size across all your buckets by setting this global value in the dashboard [here](https://supabase.com/dashboard/project/_/settings/storage). For Free projects, the limit can't exceed 50 MB. On the Pro Plan and up, you can set this value to up to 50 GB. If you need more than 50 GB, [contact us](https://supabase.com/dashboard/support/new).
You can set the max file size across all your buckets by setting this global value in the dashboard [here](https://supabase.com/dashboard/project/_/settings/storage). For Free projects, the limit can't exceed 50 MB. On the Pro Plan and up, you can set this value to up to 500 GB. If you need more than 500 GB, [contact us](https://supabase.com/dashboard/support/new).

| Plan | Max File Size Limit |
| ---------- | ------------------- |
| Free | 50 MB |
| Pro | 50 GB |
| Team | 50 GB |
| Pro | 500 GB |
| Team | 500 GB |
| Enterprise | Custom |

<Admonition type="note">
Expand Down
4 changes: 2 additions & 2 deletions apps/docs/content/guides/storage/uploads/s3-uploads.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The S3 protocol supports file upload using:

The `PutObject` action uploads the file in a single request. This matches the behavior of the Supabase SDK [Standard Upload](/docs/guides/storage/uploads/standard-uploads).

Use `PutObject` to upload smaller files, where retrying the entire upload won't be an issue. The maximum file size on paid plans is 50 GB.
Use `PutObject` to upload smaller files, where retrying the entire upload won't be an issue. The maximum file size on paid plans is 500 GB.

For example, using JavaScript and the `aws-sdk` client:

Expand All @@ -42,7 +42,7 @@ await s3Client.send(uploadCommand)

Multipart Uploads split the file into smaller parts and upload them in parallel, maximizing the upload speed on a fast network. When uploading large files, this allows you to retry the upload of individual parts in case of network issues.

This method is preferable over [Resumable Upload](/docs/guides/storage/uploads/resumable-uploads) for server-side uploads, when you want to maximize upload speed at the cost of resumability. The maximum file size on paid plans is 50 GB.
This method is preferable over [Resumable Upload](/docs/guides/storage/uploads/resumable-uploads) for server-side uploads, when you want to maximize upload speed at the cost of resumability. The maximum file size on paid plans is 500 GB.

### Upload a file in parts

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ const InfrastructureInfo = () => {
const currentPgVersion = (current_app_version ?? '')
.split('supabase-postgres-')[1]
?.replace('-orioledb', '')
const isOnNonGenerallyAvailableReleaseChannel =
current_app_version_release_channel && current_app_version_release_channel !== 'ga'
const isVisibleReleaseChannel =
current_app_version_release_channel &&
!['ga', 'withdrawn'].includes(current_app_version_release_channel)
? current_app_version_release_channel
: undefined
const isOrioleDb = useIsOrioleDb()
Expand Down Expand Up @@ -144,16 +145,16 @@ const InfrastructureInfo = () => {
value={currentPgVersion || serviceVersions?.['supabase-postgres'] || ''}
label="Postgres version"
actions={[
isOnNonGenerallyAvailableReleaseChannel && (
isVisibleReleaseChannel && (
<Tooltip>
<TooltipTrigger>
<Badge variant="warning" className="mr-1 capitalize">
{isOnNonGenerallyAvailableReleaseChannel}
{isVisibleReleaseChannel}
</Badge>
</TooltipTrigger>
<TooltipContent side="bottom" className="w-44 text-center">
This project uses a {isOnNonGenerallyAvailableReleaseChannel}{' '}
database version release
This project uses a {isVisibleReleaseChannel} database version
release
</TooltipContent>
</Tooltip>
),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES = 50 * 1024 * 1024 * 1024 // 50 GB
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN = 50 * 1024 * 1024 // 50 MB
/** Max bytes for capped storage (i.e. Pro with spend cap) */
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED = 50 * 1024 * 1024 * 1024 // 50 GB
/** Max bytes for uncapped storage (i.e. Pro without spend cap, Team, Enterprise) */
export const STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED = 500 * 1024 * 1024 * 1024 // 500 GB

export enum StorageSizeUnits {
BYTES = 'bytes',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { Clock } from 'lucide-react'
import { useEffect, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { SubmitHandler, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import * as z from 'zod'
Expand All @@ -16,6 +15,7 @@ import { useProjectStorageConfigQuery } from 'data/config/project-storage-config
import { useProjectStorageConfigUpdateUpdateMutation } from 'data/config/project-storage-config-update-mutation'
import { useCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import { formatBytes } from 'lib/helpers'
import {
Button,
FormControl_Shadcn_,
Expand All @@ -32,7 +32,12 @@ import {
Select_Shadcn_,
Switch,
} from 'ui'
import { STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, StorageSizeUnits } from './StorageSettings.constants'
import {
STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED,
STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN,
STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED,
StorageSizeUnits,
} from './StorageSettings.constants'
import { convertFromBytes, convertToBytes } from './StorageSettings.utils'

interface StorageSettingsState {
Expand All @@ -56,6 +61,8 @@ const StorageSettings = () => {

const organization = useSelectedOrganization()
const isFreeTier = organization?.plan.id === 'free'
const isSpendCapOn =
organization?.plan.id === 'pro' && organization?.usage_billing_enabled === false

const [initialValues, setInitialValues] = useState<StorageSettingsState>({
fileSizeLimit: 0,
Expand Down Expand Up @@ -84,9 +91,17 @@ const StorageSettings = () => {
}
}, [isSuccess, config])

const formattedMaxSizeBytes = `${new Intl.NumberFormat('en-US').format(
STORAGE_FILE_SIZE_LIMIT_MAX_BYTES
)} bytes`
const maxBytes = useMemo(() => {
if (organization?.plan.id === 'free') {
return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_FREE_PLAN
} else if (organization?.usage_billing_enabled) {
return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED
} else {
return STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_CAPPED
}
}, [organization])

const formattedMaxSizeBytes = `${new Intl.NumberFormat('en-US').format(maxBytes)} bytes`

const FormSchema = z
.object({
Expand All @@ -96,7 +111,7 @@ const StorageSettings = () => {
})
.superRefine((data, ctx) => {
const { unit, fileSizeLimit } = data
const { value: formattedMaxLimit } = convertFromBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES, unit)
const { value: formattedMaxLimit } = convertFromBytes(maxBytes, unit)

if (fileSizeLimit > formattedMaxLimit) {
ctx.addIssue({
Expand Down Expand Up @@ -156,7 +171,7 @@ const StorageSettings = () => {
</div>

<div className="relative flex flex-col col-span-12 gap-x-6 gap-y-2 lg:col-span-8">
<div className="grid grid-cols-12 col-span-12 gap-2 items-center">
<div className="grid grid-cols-12 col-span-12 gap-2 items-start">
<div className="col-span-8">
<FormField_Shadcn_
control={form.control}
Expand Down Expand Up @@ -222,8 +237,7 @@ const StorageSettings = () => {
limit,
storageUnit
).toLocaleString()} bytes. `}
Maximum size in bytes of a file that can be uploaded is 50 GB (
{formattedMaxSizeBytes}).
Maximum upload file size is {formatBytes(maxBytes)}.
</p>
</div>
</div>
Expand Down Expand Up @@ -261,9 +275,18 @@ const StorageSettings = () => {
{isFreeTier && (
<div className="px-6 pb-6">
<UpgradeToPro
icon={<Clock size={14} className="text-foreground-muted" />}
primaryText="Free Plan has a fixed upload file size limit of 50 MB."
secondaryText="Upgrade to the Pro Plan for a configurable upload file size limit of up to 50 GB."
secondaryText={`Upgrade to Pro Plan for a configurable upload file size limit of ${formatBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED)} and unlock image transformations.`}
source="storageSizeLimit"
/>
</div>
)}
{isSpendCapOn && (
<div className="px-6 pb-6">
<UpgradeToPro
buttonText="Disable Spend Cap"
primaryText="Reduced max upload file size limit due to Spend Cap"
secondaryText={`Disable your Spend Cap to allow file uploads of up to ${formatBytes(STORAGE_FILE_SIZE_LIMIT_MAX_BYTES_UNCAPPED)}.`}
source="storageSizeLimit"
/>
</div>
Expand Down
6 changes: 4 additions & 2 deletions apps/studio/components/ui/UpgradeToPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const UpgradeToPro = ({
},
}}
>
Reset database password
{buttonText || (plan === 'free' ? 'Upgrade to Pro' : 'Enable add on')}
</ButtonTooltip>
) : (
<Button
Expand All @@ -81,7 +81,9 @@ const UpgradeToPro = ({
href={
plan === 'free'
? `/org/${organization?.slug ?? '_'}/billing?panel=subscriptionPlan&source=${source}`
: `/project/${project?.ref ?? '_'}/settings/addons?panel=${addon}&source=${source}`
: addon == null
? `/org/${organization?.slug ?? '_'}/billing?panel=costControl&source=${source}`
: `/project/${project?.ref ?? '_'}/settings/addons?panel=${addon}&source=${source}`
}
>
{buttonText || (plan === 'free' ? 'Upgrade to Pro' : 'Enable add on')}
Expand Down
4 changes: 2 additions & 2 deletions packages/shared-data/pricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,8 @@ export const pricing: Pricing = {
title: 'Max file upload size',
plans: {
free: '50 MB',
pro: '50 GB',
team: '50 GB',
pro: '500 GB',
team: '500 GB',
enterprise: 'Custom',
},
usage_based: false,
Expand Down
Loading