Skip to content

Storage Configuration

Updated: 9/20/26, 6:22:48 PM

All platform files (site assets, reconciliation files, etc.) are stored through the S3-compatible protocol. MinIO, Aliyun OSS, Tencent COS, Huawei OBS, AWS S3 and other S3-compatible object storage services are supported. Configuration entry: Admin panel → System Config → Platform Config → Storage.

How Upload Works

Files are uploaded via presigned direct upload, in three steps:

  1. The admin frontend asks the backend for a presigned upload URL;
  2. The browser uploads the file directly to the object storage (the file never passes through the backend server);
  3. The backend confirms the file exists and registers the file record.

Therefore one action is mandatory before anything else: configure CORS rules on the bucket, otherwise "Test Connection" passes but real uploads still fail. Example on Aliyun OSS (similar on other providers: bucket → Data Security → CORS):

SettingValue
OriginYour admin panel origin, e.g. https://admin.example.com
MethodsPUT, GET, HEAD, POST
Allow Headers*
Expose HeadersETag

Parameters

ParameterRequiredDescription
EndpointYesAPI address of the storage service. Must include https:// and must NOT include the bucket name
RegionDependsSee "Do I need the region" below
Access Key / Secret KeyYesCredentials of the storage service
Path Style AccessYesAddress style switch, see below
Public Bucket / Private BucketYesBucket names; the same bucket can be used for both, see "Single bucket"
Public Base URLRecommendedExternal address for public bucket files. Do not include the bucket name (it is appended automatically)
Private Base URLNoUsually leave empty; private files are served through presigned URLs
Base Storage PathNoCommon prefix of object keys, e.g. daxpay
Upload presign expirationNoValidity of upload URLs in minutes, default 10
Download presign expirationNoValidity of private file URLs in hours, default 72

Do I need the region

The region only participates in request signing — it does not decide which data center is accessed; that is entirely determined by the endpoint. So in most cases it can be left empty:

Storage serviceRequiredExample
MinIO / self-hosted S3-compatibleNot required, leave empty
Aliyun OSSRecommendedcn-beijing, cn-hangzhou
Huawei OBSRecommendedcn-north-4
Tencent COSRequired (validates the signing region)ap-shanghai, ap-guangzhou
AWS S3Requiredus-east-1, ap-northeast-1
Qiniu KodoRequiredcn-east-1
Cloudflare R2Fixed valueauto

When left empty, us-east-1 is used for signing. MinIO and other services that do not validate the signing region are not affected. Refer to each provider's S3-compatibility documentation for exact region codes.

Path Style Access

One bucket has two address styles:

  • Path style (on): https://endpoint/bucket/object — required for MinIO and most self-hosted storage
  • Virtual-hosted style (off): https://bucket.endpoint-host/object — the native style of cloud providers

How to choose

Always enable it for self-hosted MinIO. Cloud providers usually support both; follow the recommended values in the examples below.

About Public Base URL

The final public file URL = Public Base URL / public bucket / object key. The bucket name is appended automatically, so:

  • Fill in the service address without the bucket name, e.g. https://oss-cn-beijing.aliyuncs.com
  • If the bucket is bound to a CDN / custom domain, make sure files are reachable via domain/bucket/object, otherwise images will be broken even though uploads succeeded

Single bucket

The public and private bucket fields accept the same bucket name. In that case set the bucket ACL to public-read: public files are directly accessible and private files still work through presigned URLs.

The trade-off: "private" becomes nominal — anyone holding the full URL can read the file. For production, create two separate buckets so the permission semantics actually hold.

Examples

Example 1: Self-hosted MinIO

ParameterValue
Endpointhttps://minio.example.com (the MinIO API port, not the console port)
RegionLeave empty
Path Style AccessOn
Public Base URLhttps://minio.example.com (or its public access domain)

The keys are the MinIO AccessKey / SecretKey (created via mc admin).

Example 2: Aliyun OSS (Beijing)

ParameterValue
Endpointhttps://oss-cn-beijing.aliyuncs.com
Regioncn-beijing
Path Style AccessOn
Public / Private Buckete.g. daxpay-pub / daxpay-priv (or the same bucket name)
Public Base URLhttps://oss-cn-beijing.aliyuncs.com

The keys are the AccessKey of a RAM user. Minimal permissions: PutObject, GetObject, DeleteObject, ListObjects, GetBucketInfo on both buckets.

Note: even if the backend runs on Aliyun ECS, do not switch the endpoint to the internal address (ending with -internal) — uploads go from the user's browser directly, and browsers cannot reach internal addresses.

Example 3: Tencent COS (Shanghai)

ParameterValue
Endpointhttps://cos.ap-shanghai.myqcloud.com
Regionap-shanghai (required)
Path Style AccessOff
Public Base URLhttps://cos.ap-shanghai.myqcloud.com

The keys are the SecretId / SecretKey of a Tencent Cloud sub-account.

Testing and Troubleshooting

After saving, click "Test Connection". The system runs a full loop on both buckets: bucket exists → write probe file → read → delete. A pass means read/write permissions are ready.

Common errors:

SymptomCauseFix
Test passes, upload failsBucket has no CORS rulesConfigure CORS as in "How Upload Works"
Write failed, check upload permissionKey lacks write permission / bucket policy denies writes / bucket enforces KMS encryption without KMS permissionSearch 探测写入失败 (probe write failed) in backend logs for the real status code; temporarily grant full permissions to narrow it down
File uploaded but image not displayedPublic Base URL contains the bucket name, or the public bucket is privateRemove the bucket name from the URL; set the public bucket to public-read
Upload succeeds but the record stays pendingKey can write but cannot readAdd read permission (GetObject)

WARNING

"A record exists in the file list" does not mean the upload succeeded — the record is created as soon as the upload URL is requested. Only a truly accessible file counts.

Official Website · Released under the GNU LGPL v3.0